[Pkg-voip-commits] [asterisk] 21/34: Imported Upstream version 13.7.2~dfsg

Jonas Smedegaard dr at jones.dk
Sun Mar 20 18:39:22 UTC 2016


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

js pushed a commit to branch master
in repository asterisk.

commit b5d03432407a315a66b59d034deeff1fc8f28f71
Author: Jonas Smedegaard <dr at jones.dk>
Date:   Sun Mar 20 07:27:56 2016 +0100

    Imported Upstream version 13.7.2~dfsg
---
 .gitreview                                         |     4 +
 .version                                           |     2 +-
 CHANGES                                            |   314 +-
 ChangeLog                                          | 13622 ++++++++++++++++++-
 Makefile                                           |   117 +-
 Makefile.moddir_rules                              |     5 +-
 Makefile.rules                                     |    32 +
 UPGRADE.txt                                        |    96 +
 addons/cdr_mysql.c                                 |     2 +-
 addons/chan_mobile.c                               |     4 +-
 addons/chan_ooh323.c                               |    15 +-
 addons/format_mp3.c                                |     2 +-
 addons/ooh323c/src/ooSocket.c                      |     2 +-
 addons/ooh323c/src/ooh245.c                        |     2 +-
 addons/ooh323c/src/ooq931.c                        |     6 +-
 addons/ooh323c/src/printHandler.c                  |     2 +-
 addons/res_config_mysql.c                          |    68 +-
 apps/Makefile                                      |     4 +-
 apps/app_adsiprog.c                                |     4 +-
 apps/app_agent_pool.c                              |    43 +-
 apps/app_alarmreceiver.c                           |     4 +-
 apps/app_amd.c                                     |     5 +-
 apps/app_authenticate.c                            |     2 +-
 apps/app_bridgewait.c                              |     2 +-
 apps/app_cdr.c                                     |     2 +-
 apps/app_celgenuserevent.c                         |     2 +-
 apps/app_chanisavail.c                             |     8 +-
 apps/app_channelredirect.c                         |     2 +-
 apps/app_chanspy.c                                 |     5 +-
 apps/app_confbridge.c                              |   356 +-
 apps/app_controlplayback.c                         |     2 +-
 apps/app_dahdiras.c                                |     2 +-
 apps/app_db.c                                      |     2 +-
 apps/app_dial.c                                    |   316 +-
 apps/app_dictate.c                                 |     6 +-
 apps/app_directed_pickup.c                         |     2 +-
 apps/app_directory.c                               |    50 +-
 apps/app_disa.c                                    |     2 +-
 apps/app_dumpchan.c                                |     4 +-
 apps/app_echo.c                                    |     2 +-
 apps/app_exec.c                                    |     2 +-
 apps/app_externalivr.c                             |     4 +-
 apps/app_fax.c                                     |     2 +-
 apps/app_festival.c                                |     2 +-
 apps/app_flash.c                                   |     2 +-
 apps/app_followme.c                                |    15 +-
 apps/app_forkcdr.c                                 |     2 +-
 apps/app_getcpeid.c                                |    12 +-
 apps/app_ices.c                                    |     2 +-
 apps/app_image.c                                   |     2 +-
 apps/app_ivrdemo.c                                 |     2 +-
 apps/app_jack.c                                    |     2 +-
 apps/app_macro.c                                   |    33 +-
 apps/app_meetme.c                                  |    40 +-
 apps/app_milliwatt.c                               |     2 +-
 apps/app_minivm.c                                  |    12 +-
 apps/app_mixmonitor.c                              |     9 +-
 apps/app_morsecode.c                               |     2 +-
 apps/app_mp3.c                                     |     2 +-
 apps/app_nbscat.c                                  |     2 +-
 apps/app_originate.c                               |     2 +-
 apps/app_osplookup.c                               |     6 +-
 apps/app_page.c                                    |    32 +-
 apps/app_playback.c                                |     6 +-
 apps/app_playtones.c                               |     2 +-
 apps/app_privacy.c                                 |     2 +-
 apps/app_queue.c                                   |   611 +-
 apps/app_read.c                                    |     2 +-
 apps/app_readexten.c                               |     2 +-
 apps/app_record.c                                  |     5 +-
 apps/app_saycounted.c                              |     2 +-
 apps/app_sayunixtime.c                             |     2 +-
 apps/app_senddtmf.c                                |     2 +-
 apps/app_sendtext.c                                |     2 +-
 apps/app_setcallerid.c                             |     2 +-
 apps/app_skel.c                                    |     2 +-
 apps/app_sms.c                                     |    16 +-
 apps/app_softhangup.c                              |     2 +-
 apps/app_speech_utils.c                            |     2 +-
 apps/app_stack.c                                   |     2 +-
 apps/app_stasis.c                                  |    44 +-
 apps/app_system.c                                  |     2 +-
 apps/app_talkdetect.c                              |     2 +-
 apps/app_test.c                                    |     2 +-
 apps/app_transfer.c                                |     2 +-
 apps/app_url.c                                     |     2 +-
 apps/app_userevent.c                               |     2 +-
 apps/app_verbose.c                                 |     2 +-
 apps/app_voicemail.c                               |   113 +-
 apps/app_waitforring.c                             |     2 +-
 apps/app_waitforsilence.c                          |     2 +-
 apps/app_waituntil.c                               |     2 +-
 apps/app_while.c                                   |     2 +-
 apps/app_zapateller.c                              |     2 +-
 apps/confbridge/conf_chan_announce.c               |     2 +-
 apps/confbridge/conf_chan_record.c                 |     9 +-
 apps/confbridge/conf_config_parser.c               |    45 +-
 apps/confbridge/confbridge_manager.c               |     2 +-
 apps/confbridge/include/confbridge.h               |    18 +-
 asterisk-13.1.1-summary.html                       |    65 -
 asterisk-13.1.1-summary.txt                        |    96 -
 asterisk-13.7.2-summary.html                       |    27 +
 asterisk-13.7.2-summary.txt                        |   131 +
 autoconf/ast_check_raii.m4                         |    56 +
 autoconf/ast_check_strsep_array_bounds.m4          |    81 +
 autoconf/ast_gcc_attribute.m4                      |     2 +-
 bridges/bridge_builtin_features.c                  |    10 +-
 bridges/bridge_builtin_interval_features.c         |     2 +-
 bridges/bridge_holding.c                           |    22 +-
 bridges/bridge_native_rtp.c                        |    61 +-
 bridges/bridge_simple.c                            |    16 +-
 bridges/bridge_softmix.c                           |   193 +-
 build_tools/cflags.xml                             |    33 +
 build_tools/get_moduleinfo                         |     5 +-
 build_tools/make_buildopts_h                       |    32 +-
 build_tools/make_version                           |     7 +-
 build_tools/make_version_c                         |    25 +
 build_tools/mkpkgconfig                            |     9 +-
 cdr/cdr_adaptive_odbc.c                            |     2 +-
 cdr/cdr_csv.c                                      |    12 +-
 cdr/cdr_custom.c                                   |     2 +-
 cdr/cdr_manager.c                                  |   128 +-
 cdr/cdr_odbc.c                                     |    34 +-
 cdr/cdr_pgsql.c                                    |    46 +-
 cdr/cdr_radius.c                                   |     2 +-
 cdr/cdr_sqlite.c                                   |     2 +-
 cdr/cdr_sqlite3_custom.c                           |     2 +-
 cdr/cdr_syslog.c                                   |     2 +-
 cdr/cdr_tds.c                                      |     2 +-
 cel/cel_custom.c                                   |     2 +-
 cel/cel_manager.c                                  |   162 +-
 cel/cel_odbc.c                                     |     2 +-
 cel/cel_pgsql.c                                    |     6 +-
 cel/cel_radius.c                                   |     2 +-
 cel/cel_sqlite3_custom.c                           |     3 +-
 cel/cel_tds.c                                      |     2 +-
 channels/Makefile                                  |    10 +-
 channels/chan_alsa.c                               |     4 +-
 channels/chan_bridge_media.c                       |     2 +-
 channels/chan_console.c                            |     4 +-
 channels/chan_dahdi.c                              |   257 +-
 channels/chan_dahdi.h                              |     2 +-
 channels/chan_iax2.c                               |   414 +-
 channels/chan_mgcp.c                               |    30 +-
 channels/chan_misdn.c                              |     8 +-
 channels/chan_motif.c                              |     4 +-
 channels/chan_multicast_rtp.c                      |     2 +-
 channels/chan_nbs.c                                |     4 +-
 channels/chan_oss.c                                |     4 +-
 channels/chan_phone.c                              |     4 +-
 channels/chan_pjsip.c                              |   287 +-
 channels/chan_sip.c                                |   743 +-
 channels/chan_skinny.c                             |   135 +-
 channels/chan_unistim.c                            |    68 +-
 channels/chan_vpb.cc                               |     9 +-
 channels/console_board.c                           |     4 +-
 channels/console_gui.c                             |     2 +-
 channels/console_video.c                           |     4 +-
 channels/console_video.h                           |     2 +-
 channels/dahdi/bridge_native_dahdi.c               |    17 +-
 channels/iax2/codec_pref.c                         |     2 +-
 channels/iax2/firmware.c                           |     2 +-
 channels/iax2/format_compatibility.c               |     2 +-
 channels/iax2/parser.c                             |     4 +-
 channels/iax2/provision.c                          |     2 +-
 channels/misdn/Makefile                            |     2 +-
 channels/misdn/ie.c                                |    14 +-
 channels/misdn_config.c                            |     2 +-
 channels/pjsip/dialplan_functions.c                |    61 +-
 channels/sig_analog.c                              |   199 +-
 channels/sig_analog.h                              |     1 +
 channels/sig_pri.c                                 |   150 +-
 channels/sig_pri.h                                 |     2 +
 channels/sip/config_parser.c                       |    11 +-
 channels/sip/dialplan_functions.c                  |     4 +-
 channels/sip/include/dialog.h                      |    15 +-
 channels/sip/include/route.h                       |     2 +-
 channels/sip/include/security_events.h             |     3 +-
 channels/sip/include/sip.h                         |    16 +-
 channels/sip/reqresp_parser.c                      |     4 +-
 channels/sip/route.c                               |     2 +-
 channels/sip/security_events.c                     |     7 +-
 channels/vcodecs.c                                 |     8 +-
 channels/vgrabbers.c                               |     4 +-
 codecs/codec_a_mu.c                                |     2 +-
 codecs/codec_adpcm.c                               |     2 +-
 codecs/codec_alaw.c                                |     2 +-
 codecs/codec_dahdi.c                               |     2 +-
 codecs/codec_g722.c                                |     2 +-
 codecs/codec_g726.c                                |     2 +-
 codecs/codec_gsm.c                                 |    31 +-
 codecs/codec_ilbc.c                                |    30 +-
 codecs/codec_lpc10.c                               |    43 +-
 codecs/codec_resample.c                            |    10 +-
 codecs/codec_speex.c                               |    62 +-
 codecs/codec_ulaw.c                                |     2 +-
 codecs/g722/g722.h                                 |     2 +-
 codecs/g722/g722_decode.c                          |     2 +-
 codecs/g722/g722_encode.c                          |     2 +-
 codecs/gsm/Makefile                                |     2 +-
 codecs/gsm/src/gsm_create.c                        |     2 -
 configs/basic-pbx/README                           |    15 +
 configs/basic-pbx/asterisk.conf                    |    26 +
 configs/basic-pbx/cdr.conf                         |     7 +
 configs/basic-pbx/cdr_custom.conf                  |     4 +
 configs/basic-pbx/confbridge.conf                  |     1 +
 configs/basic-pbx/extensions.conf                  |   193 +
 configs/basic-pbx/indications.conf                 |    19 +
 configs/basic-pbx/logger.conf                      |     9 +
 configs/basic-pbx/modules.conf                     |   115 +
 configs/basic-pbx/musiconhold.conf                 |     5 +
 configs/basic-pbx/pjsip.conf                       |   332 +
 configs/basic-pbx/queues.conf                      |    19 +
 configs/basic-pbx/voicemail.conf                   |    23 +
 configs/samples/amd.conf.sample                    |     1 +
 configs/samples/cdr.conf.sample                    |     2 +
 configs/samples/cdr_odbc.conf.sample               |     1 +
 configs/samples/chan_dahdi.conf.sample             |    16 +
 configs/samples/confbridge.conf.sample             |     6 +
 configs/samples/extensions_minivm.conf.sample      |     2 +-
 configs/samples/features.conf.sample               |     5 +-
 configs/samples/http.conf.sample                   |    30 +
 configs/samples/iax.conf.sample                    |     7 -
 configs/samples/pjsip.conf.sample                  |    58 +-
 configs/samples/pjsip_wizard.conf.sample           |   147 +
 configs/samples/queues.conf.sample                 |    14 +-
 configs/samples/res_fax.conf.sample                |     4 +
 configs/samples/sip.conf.sample                    |    10 +-
 configure                                          |   677 +-
 configure.ac                                       |    71 +-
 .../versions/154177371065_add_default_from_user.py |    22 +
 .../189a235b3fd7_add_keep_alive_interval.py        |    22 +
 .../versions/23530d604b96_add_rpid_immediate.py    |    48 +
 .../26f10cadc157_add_pjsip_timeout_options.py      |    24 +
 .../versions/28b8e71e541f_add_g726_non_standard.py |    30 +
 .../28ce1e718f05_add_fatal_response_interval.py    |    22 +
 .../2d078ec071b7_increaes_contact_column_size.py   |    22 +
 .../versions/31cd4f4891ec_add_auto_dtmf_mode.py    |    64 +
 ...1a3bf4143e_add_user_eq_phone_option_to_pjsip.py |    30 +
 ...f47c6c44_add_pjsip_endpoint_identifier_order.py |    21 +
 .../461d7d691209_add_pjsip_qualify_timeout.py      |    25 +
 .../versions/498357a710ae_add_rtp_keepalive.py     |    22 +
 ...41e0b5e89_add_pjsip_max_initial_qualify_time.py |    20 +
 contrib/editors/asterisk.vim                       |     4 +-
 contrib/init.d/rc.debian.asterisk                  |     2 +-
 contrib/init.d/rc.gentoo.asterisk                  |     2 +-
 contrib/init.d/rc.mandriva.asterisk                |     2 +-
 contrib/init.d/rc.mandriva.zaptel                  |     2 +-
 contrib/init.d/rc.redhat.asterisk                  |     2 +-
 contrib/init.d/rc.slackware.asterisk               |     2 +-
 .../realtime/{sqlserver => mssql}/mssql_cdr.sql    |     0
 .../realtime/{sqlserver => mssql}/mssql_config.sql |   108 +-
 .../{sqlserver => mssql}/mssql_voicemail.sql       |     0
 contrib/realtime/mysql/mysql_config.sql            |    82 +
 contrib/realtime/oracle/oracle_config.sql          |   108 +-
 contrib/realtime/postgresql/postgresql_config.sql  |    62 +-
 contrib/scripts/ast_grab_core                      |     2 +-
 contrib/scripts/astversion                         |   536 +
 contrib/scripts/autosupport                        |    95 +-
 contrib/scripts/clang-scan-build                   |   136 +
 contrib/scripts/dbsep.cgi                          |     2 +-
 contrib/scripts/install_prereq                     |    11 +-
 contrib/scripts/retrieve_extensions_from_sql.pl    |     2 +-
 contrib/scripts/sip_to_pjsip/astconfigparser.py    |    15 +-
 contrib/scripts/sip_to_pjsip/sip_to_pjsip.py       |    23 +-
 contrib/utils/eagi_proxy.c                         |     2 +-
 doc/appdocsxml.xslt                                |     6 +
 .../asterisk-ng-doxygen.in                         |     0
 formats/format_g719.c                              |     2 +-
 formats/format_g723.c                              |     2 +-
 formats/format_g726.c                              |     2 +-
 formats/format_g729.c                              |     2 +-
 formats/format_gsm.c                               |     2 +-
 formats/format_h263.c                              |     2 +-
 formats/format_h264.c                              |     2 +-
 formats/format_ilbc.c                              |     2 +-
 formats/format_jpeg.c                              |     2 +-
 formats/format_ogg_vorbis.c                        |     2 +-
 formats/format_pcm.c                               |     2 +-
 formats/format_siren14.c                           |     2 +-
 formats/format_siren7.c                            |     2 +-
 formats/format_sln.c                               |     2 +-
 formats/format_vox.c                               |     2 +-
 formats/format_wav.c                               |    55 +-
 formats/format_wav_gsm.c                           |    18 +-
 funcs/func_aes.c                                   |     2 +-
 funcs/func_base64.c                                |     2 +-
 funcs/func_blacklist.c                             |     2 +-
 funcs/func_callcompletion.c                        |     2 +-
 funcs/func_callerid.c                              |    41 +-
 funcs/func_cdr.c                                   |    11 +-
 funcs/func_channel.c                               |    73 +-
 funcs/func_config.c                                |    48 +-
 funcs/func_curl.c                                  |     7 +-
 funcs/func_cut.c                                   |     2 +-
 funcs/func_db.c                                    |     2 +-
 funcs/func_devstate.c                              |     2 +-
 funcs/func_dialgroup.c                             |     2 +-
 funcs/func_dialplan.c                              |     2 +-
 funcs/func_enum.c                                  |     2 +-
 funcs/func_env.c                                   |     6 +-
 funcs/func_extstate.c                              |     2 +-
 funcs/func_frame_trace.c                           |     2 +-
 funcs/func_global.c                                |     2 +-
 funcs/func_groupcount.c                            |     4 +-
 funcs/func_hangupcause.c                           |     4 +-
 funcs/func_holdintercept.c                         |   236 +
 funcs/func_iconv.c                                 |     2 +-
 funcs/func_jitterbuffer.c                          |     2 +-
 funcs/func_lock.c                                  |     2 +-
 funcs/func_logic.c                                 |     2 +-
 funcs/func_math.c                                  |    14 +-
 funcs/func_md5.c                                   |     2 +-
 funcs/func_module.c                                |     2 +-
 funcs/func_odbc.c                                  |     2 +-
 funcs/func_periodic_hook.c                         |    12 +-
 funcs/func_pitchshift.c                            |     2 +-
 funcs/func_pjsip_aor.c                             |   186 +
 funcs/func_pjsip_contact.c                         |   203 +
 funcs/func_pjsip_endpoint.c                        |     2 +-
 funcs/func_presencestate.c                         |    18 +-
 funcs/func_rand.c                                  |     2 +-
 funcs/func_realtime.c                              |     2 +-
 funcs/func_sha1.c                                  |     2 +-
 funcs/func_shell.c                                 |     2 +-
 funcs/func_speex.c                                 |     2 +-
 funcs/func_sprintf.c                               |     2 +-
 funcs/func_srv.c                                   |     2 +-
 funcs/func_strings.c                               |     2 +-
 funcs/func_sysinfo.c                               |     2 +-
 funcs/func_talkdetect.c                            |     2 +-
 funcs/func_timeout.c                               |     2 +-
 funcs/func_uri.c                                   |     2 +-
 funcs/func_version.c                               |     2 +-
 funcs/func_vmcount.c                               |     2 +-
 funcs/func_volume.c                                |     2 +-
 include/asterisk.h                                 |    64 +-
 include/asterisk/_private.h                        |    29 +-
 include/asterisk/app.h                             |    18 +
 include/asterisk/ari.h                             |     5 +
 include/asterisk/ast_version.h                     |     3 +
 include/asterisk/audiohook.h                       |     1 +
 include/asterisk/autoconfig.h.in                   |    28 +-
 include/asterisk/bridge.h                          |    26 +-
 include/asterisk/bridge_channel.h                  |    19 +
 include/asterisk/bridge_channel_internal.h         |    59 +-
 include/asterisk/bridge_internal.h                 |     3 +
 include/asterisk/bridge_technology.h               |     5 +-
 include/asterisk/cel.h                             |     4 +-
 include/asterisk/channel.h                         |    66 +-
 include/asterisk/config.h                          |    55 +-
 include/asterisk/dial.h                            |     1 +
 include/asterisk/dsp.h                             |     3 +
 include/asterisk/endpoints.h                       |    10 +
 include/asterisk/format.h                          |    34 +
 include/asterisk/format_cap.h                      |     5 +
 include/asterisk/global_datastores.h               |     6 -
 include/asterisk/http.h                            |    22 +
 include/asterisk/http_websocket.h                  |    86 +-
 include/asterisk/inline_api.h                      |    12 +-
 include/asterisk/json.h                            |    18 +-
 include/asterisk/lock.h                            |    49 +-
 include/asterisk/logger.h                          |    57 +-
 include/asterisk/manager.h                         |    53 +-
 include/asterisk/max_forwards.h                    |    78 +
 include/asterisk/module.h                          |    92 +-
 include/asterisk/monitor.h                         |    20 -
 include/asterisk/pbx.h                             |    16 +-
 include/asterisk/poll-compat.h                     |     2 +-
 include/asterisk/res_fax.h                         |     6 +-
 include/asterisk/res_pjsip.h                       |   370 +-
 include/asterisk/res_pjsip_cli.h                   |     2 +-
 include/asterisk/res_pjsip_presence_xml.h          |     9 +-
 include/asterisk/res_pjsip_pubsub.h                |    19 +
 include/asterisk/res_pjsip_session.h               |    34 +-
 include/asterisk/rtp_engine.h                      |   132 +-
 include/asterisk/sched.h                           |    11 +
 include/asterisk/sem.h                             |    19 +
 include/asterisk/sip_api.h                         |     1 -
 include/asterisk/slin.h                            |     4 +-
 include/asterisk/sorcery.h                         |   139 +
 include/asterisk/stasis.h                          |    11 +
 include/asterisk/stasis_app.h                      |    29 +-
 include/asterisk/stasis_endpoints.h                |     6 +
 include/asterisk/statsd.h                          |    71 +-
 include/asterisk/strings.h                         |    57 +-
 include/asterisk/syslog.h                          |     2 +
 include/asterisk/taskprocessor.h                   |     8 +
 include/asterisk/tcptls.h                          |    10 +-
 include/asterisk/term.h                            |     4 +-
 include/asterisk/test.h                            |    35 +-
 include/asterisk/threadpool.h                      |    75 +
 include/asterisk/threadstorage.h                   |    43 +
 include/asterisk/translate.h                       |     8 +
 include/asterisk/utils.h                           |   112 +-
 include/asterisk/vector.h                          |   504 +-
 main/Makefile                                      |     5 +-
 main/abstract_jb.c                                 |     2 +-
 main/acl.c                                         |     2 +-
 main/alaw.c                                        |     2 +-
 main/aoc.c                                         |   112 +-
 main/app.c                                         |    60 +-
 main/ast_expr2.c                                   |     2 +-
 main/ast_expr2.fl                                  |     2 +-
 main/ast_expr2.y                                   |     2 +-
 main/ast_expr2f.c                                  |     2 +-
 main/asterisk.c                                    |   497 +-
 main/astfd.c                                       |    61 +-
 main/astmm.c                                       |     4 +-
 main/astobj2.c                                     |    18 +-
 main/astobj2_container.c                           |    10 +-
 main/astobj2_hash.c                                |    15 +-
 main/astobj2_rbtree.c                              |     4 +-
 main/audiohook.c                                   |   211 +-
 main/autochan.c                                    |     2 +-
 main/autoservice.c                                 |     2 +-
 main/backtrace.c                                   |     2 +-
 main/bridge.c                                      |   238 +-
 main/bridge_after.c                                |     3 +-
 main/bridge_basic.c                                |   245 +-
 main/bridge_channel.c                              |   199 +-
 main/bridge_roles.c                                |     2 +-
 main/bucket.c                                      |     9 +-
 main/callerid.c                                    |     2 +-
 main/ccss.c                                        |    10 +-
 main/cdr.c                                         |    26 +-
 main/cel.c                                         |    46 +-
 main/channel.c                                     |   368 +-
 main/channel_internal_api.c                        |    17 +-
 main/chanvars.c                                    |     2 +-
 main/cli.c                                         |    34 +-
 main/codec.c                                       |    13 +-
 main/codec_builtin.c                               |     6 +-
 main/config.c                                      |   121 +-
 main/config_options.c                              |     8 +-
 main/core_local.c                                  |    15 +-
 main/core_unreal.c                                 |    14 +-
 main/crypt.c                                       |     2 +-
 main/data.c                                        |     4 +-
 main/datastore.c                                   |     2 +-
 main/db.c                                          |    15 +-
 main/devicestate.c                                 |    11 +-
 main/dial.c                                        |    57 +-
 main/dns.c                                         |     2 +-
 main/dnsmgr.c                                      |     4 +-
 main/dsp.c                                         |    36 +-
 main/editline/np/strlcat.c                         |     8 -
 main/editline/np/strlcpy.c                         |    10 -
 main/endpoints.c                                   |    21 +-
 main/enum.c                                        |     4 +-
 main/event.c                                       |     4 +-
 main/features.c                                    |    40 +-
 main/file.c                                        |    10 +-
 main/fixedjitterbuf.c                              |     2 +-
 main/format.c                                      |    47 +-
 main/format_cache.c                                |     5 +-
 main/format_cap.c                                  |    74 +-
 main/format_compatibility.c                        |     2 +-
 main/frame.c                                       |     2 +-
 main/framehook.c                                   |     4 +-
 main/fskmodem_float.c                              |     2 +-
 main/fskmodem_int.c                                |     2 +-
 main/global_datastores.c                           |    58 +-
 main/hashtab.c                                     |     8 +-
 main/heap.c                                        |     2 +-
 main/http.c                                        |   196 +-
 main/image.c                                       |     4 +-
 main/indications.c                                 |     9 +-
 main/io.c                                          |     2 +-
 main/jitterbuf.c                                   |     4 +-
 main/json.c                                        |    31 +-
 main/libasteriskssl.c                              |    37 +-
 main/loader.c                                      |   152 +-
 main/lock.c                                        |   572 +-
 main/logger.c                                      |   404 +-
 main/manager.c                                     |   158 +-
 main/manager_bridges.c                             |    76 +-
 main/manager_channels.c                            |    62 +-
 main/manager_endpoints.c                           |     5 +-
 main/manager_mwi.c                                 |     4 +-
 main/manager_system.c                              |     4 +-
 main/max_forwards.c                                |   165 +
 main/md5.c                                         |     2 +-
 main/message.c                                     |    12 +-
 main/mixmonitor.c                                  |     2 +-
 main/named_acl.c                                   |     7 +-
 main/netsock.c                                     |     2 +-
 main/netsock2.c                                    |     6 +-
 main/optional_api.c                                |     2 +-
 main/parking.c                                     |     2 +-
 main/pbx.c                                         |   714 +-
 main/pickup.c                                      |     4 +-
 main/plc.c                                         |     2 +-
 main/poll.c                                        |     2 +-
 main/presencestate.c                               |    19 +-
 main/privacy.c                                     |     2 +-
 main/rtp_engine.c                                  |   280 +-
 main/say.c                                         |     2 +-
 main/sched.c                                       |   189 +-
 main/sdp_srtp.c                                    |   112 +-
 main/security_events.c                             |     9 +-
 main/sem.c                                         |    35 +-
 main/slinfactory.c                                 |     2 +-
 main/smoother.c                                    |     2 +-
 main/sorcery.c                                     |   393 +-
 main/sounds_index.c                                |     2 +-
 main/srv.c                                         |     2 +-
 main/stasis.c                                      |    20 +-
 main/stasis_bridges.c                              |     5 +-
 main/stasis_cache.c                                |     2 +-
 main/stasis_cache_pattern.c                        |     2 +-
 main/stasis_channels.c                             |   157 +-
 main/stasis_endpoints.c                            |   145 +-
 main/stasis_message.c                              |     2 +-
 main/stasis_message_router.c                       |     6 +-
 main/stasis_system.c                               |     2 +-
 main/stdtime/localtime.c                           |   295 +-
 main/strings.c                                     |    99 +-
 main/stun.c                                        |     4 +-
 main/syslog.c                                      |    10 +-
 main/taskprocessor.c                               |    35 +-
 main/tcptls.c                                      |    59 +-
 main/tdd.c                                         |     2 +-
 main/term.c                                        |    30 +-
 main/test.c                                        |    82 +-
 main/threadpool.c                                  |   220 +-
 main/threadstorage.c                               |     4 +-
 main/timing.c                                      |     4 +-
 main/translate.c                                   |   162 +-
 main/udptl.c                                       |    30 +-
 main/ulaw.c                                        |     2 +-
 main/utils.c                                       |   208 +-
 main/uuid.c                                        |     2 +-
 main/xml.c                                         |     2 +-
 main/xmldoc.c                                      |   196 +-
 makeopts.in                                        |     3 +
 menuselect/configure                               |     8 +-
 menuselect/configure.ac                            |     2 +-
 mkinstalldirs                                      |     2 +-
 pbx/Makefile                                       |     2 +-
 pbx/dundi-parser.c                                 |    10 +-
 pbx/pbx_ael.c                                      |     2 +-
 pbx/pbx_config.c                                   |    30 +-
 pbx/pbx_dundi.c                                    |     3 +-
 pbx/pbx_loopback.c                                 |     2 +-
 pbx/pbx_lua.c                                      |     2 +-
 pbx/pbx_realtime.c                                 |     2 +-
 pbx/pbx_spool.c                                    |    39 +-
 res/Makefile                                       |    11 +-
 res/ael/ael.flex                                   |     2 +-
 res/ael/ael.tab.c                                  |     2 +-
 res/ael/ael.y                                      |     2 +-
 res/ael/ael_lex.c                                  |     2 +-
 res/ael/pval.c                                     |     2 +-
 res/ari.make                                       |    11 +
 res/ari/ari_model_validators.c                     |   870 +-
 res/ari/ari_model_validators.h                     |   211 +
 res/ari/ari_websockets.c                           |    21 +-
 res/ari/cli.c                                      |     2 +-
 res/ari/config.c                                   |    74 +-
 res/ari/resource_applications.c                    |     2 +-
 res/ari/resource_asterisk.c                        |   616 +-
 res/ari/resource_asterisk.h                        |   190 +
 res/ari/resource_bridges.c                         |    24 +-
 res/ari/resource_bridges.h                         |    12 +-
 res/ari/resource_channels.c                        |   442 +-
 res/ari/resource_channels.h                        |    58 +-
 res/ari/resource_device_states.c                   |     7 +-
 res/ari/resource_endpoints.c                       |    63 +-
 res/ari/resource_events.c                          |    63 +-
 res/ari/resource_events.h                          |    17 +-
 res/ari/resource_mailboxes.c                       |     7 +-
 res/ari/resource_playbacks.c                       |     7 +-
 res/ari/resource_recordings.c                      |     7 +-
 res/ari/resource_sounds.c                          |     2 +-
 res/parking/parking_applications.c                 |    85 +-
 res/parking/parking_bridge_features.c              |     2 +-
 res/parking/parking_manager.c                      |    96 +-
 res/parking/parking_tests.c                        |    11 +-
 res/parking/parking_ui.c                           |     2 +-
 res/res_adsi.c                                     |     2 +-
 res/res_ael_share.c                                |     2 +-
 res/res_agi.c                                      |    13 +-
 res/res_ari.c                                      |    28 +-
 res/res_ari_applications.c                         |     3 +-
 res/res_ari_asterisk.c                             |  1043 +-
 res/res_ari_bridges.c                              |    17 +-
 res/res_ari_channels.c                             |   157 +-
 res/res_ari_device_states.c                        |     3 +-
 res/res_ari_endpoints.c                            |     8 +-
 res/res_ari_events.c                               |   119 +-
 res/res_ari_mailboxes.c                            |     3 +-
 res/res_ari_model.c                                |     2 +-
 res/res_ari_playbacks.c                            |     3 +-
 res/res_ari_recordings.c                           |     3 +-
 res/res_ari_sounds.c                               |     3 +-
 res/res_calendar.c                                 |    12 +-
 res/res_calendar_caldav.c                          |     2 +-
 res/res_calendar_ews.c                             |     2 +-
 res/res_calendar_exchange.c                        |     2 +-
 res/res_calendar_icalendar.c                       |     2 +-
 res/res_chan_stats.c                               |     6 +-
 res/res_clialiases.c                               |     2 +-
 res/res_clioriginate.c                             |     2 +-
 res/res_config_curl.c                              |     2 +-
 res/res_config_ldap.c                              |     2 +-
 res/res_config_odbc.c                              |    10 +-
 res/res_config_pgsql.c                             |    10 +-
 res/res_config_sqlite.c                            |    10 +-
 res/res_config_sqlite3.c                           |     2 +-
 res/res_convert.c                                  |     2 +-
 res/res_corosync.c                                 |    16 +-
 res/res_crypto.c                                   |     4 +-
 res/res_curl.c                                     |     2 +-
 res/res_endpoint_stats.c                           |   157 +
 res/res_fax.c                                      |   148 +-
 res/res_fax_spandsp.c                              |    24 +-
 res/res_format_attr_celt.c                         |     2 +-
 res/res_format_attr_h263.c                         |     2 +-
 res/res_format_attr_h264.c                         |    22 +-
 res/res_format_attr_opus.c                         |   222 +-
 res/res_format_attr_silk.c                         |    28 +-
 res/res_format_attr_vp8.c                          |   228 +
 res/res_hep.c                                      |     2 +-
 res/res_hep_pjsip.c                                |     2 +-
 res/res_hep_rtcp.c                                 |     4 +-
 res/res_http_post.c                                |     2 +-
 res/res_http_websocket.c                           |   257 +-
 res/res_limit.c                                    |     2 +-
 res/res_manager_devicestate.c                      |     8 +-
 res/res_manager_presencestate.c                    |     8 +-
 res/res_monitor.c                                  |     3 +-
 res/res_musiconhold.c                              |     4 +-
 res/res_mutestream.c                               |     2 +-
 res/res_mwi_external.c                             |     2 +-
 res/res_mwi_external_ami.c                         |    16 +-
 res/res_odbc.c                                     |    29 +-
 res/res_parking.c                                  |     2 +-
 res/res_phoneprov.c                                |    10 +-
 res/res_pjsip.c                                    |   986 +-
 res/res_pjsip/config_auth.c                        |    18 +-
 res/res_pjsip/config_domain_aliases.c              |     1 +
 res/res_pjsip/config_global.c                      |   219 +-
 res/res_pjsip/config_system.c                      |    37 +-
 res/res_pjsip/config_transport.c                   |    64 +-
 res/res_pjsip/include/res_pjsip_private.h          |   210 +-
 res/res_pjsip/location.c                           |   308 +-
 res/res_pjsip/pjsip_cli.c                          |    40 +-
 res/res_pjsip/pjsip_configuration.c                |   340 +-
 res/res_pjsip/pjsip_distributor.c                  |   138 +-
 res/res_pjsip/pjsip_global_headers.c               |    20 +-
 res/res_pjsip/pjsip_options.c                      |   321 +-
 res/res_pjsip/pjsip_outbound_auth.c                |     9 +-
 res/res_pjsip/presence_xml.c                       |    43 +-
 res/res_pjsip/security_events.c                    |     2 +-
 res/res_pjsip_acl.c                                |     3 +
 res/res_pjsip_caller_id.c                          |    73 +-
 res/res_pjsip_config_wizard.c                      |  1202 ++
 res/res_pjsip_dialog_info_body_generator.c         |     9 +-
 res/res_pjsip_diversion.c                          |     9 +-
 res/res_pjsip_dlg_options.c                        |   107 +
 res/res_pjsip_dtmf_info.c                          |     8 +-
 res/res_pjsip_endpoint_identifier_anonymous.c      |     2 +-
 res/res_pjsip_endpoint_identifier_ip.c             |    60 +-
 res/res_pjsip_endpoint_identifier_user.c           |     2 +-
 res/res_pjsip_exten_state.c                        |    48 +-
 res/res_pjsip_keepalive.c                          |   269 +
 res/res_pjsip_log_forwarder.c                      |     2 +-
 res/res_pjsip_logger.c                             |     2 +-
 res/res_pjsip_messaging.c                          |    54 +-
 res/res_pjsip_multihomed.c                         |    79 +-
 res/res_pjsip_mwi.c                                |   351 +-
 res/res_pjsip_nat.c                                |    98 +-
 res/res_pjsip_notify.c                             |     3 +-
 res/res_pjsip_outbound_authenticator_digest.c      |    25 +-
 res/res_pjsip_outbound_publish.c                   |   591 +-
 res/res_pjsip_outbound_registration.c              |  1035 +-
 res/res_pjsip_phoneprov_provider.c                 |   159 +-
 res/res_pjsip_pidf_body_generator.c                |    11 +-
 res/res_pjsip_pidf_digium_body_supplement.c        |     2 +-
 res/res_pjsip_publish_asterisk.c                   |     1 +
 res/res_pjsip_pubsub.c                             |   468 +-
 res/res_pjsip_pubsub.exports.in                    |     2 +
 res/res_pjsip_refer.c                              |   337 +-
 res/res_pjsip_registrar.c                          |    24 +-
 res/res_pjsip_registrar_expire.c                   |   164 +-
 res/res_pjsip_rfc3326.c                            |    17 +-
 res/res_pjsip_sdp_rtp.c                            |   277 +-
 res/res_pjsip_send_to_voicemail.c                  |    10 +-
 res/res_pjsip_session.c                            |   819 +-
 res/res_pjsip_session.exports.in                   |     2 +
 res/res_pjsip_sips_contact.c                       |   107 +
 res/res_pjsip_t38.c                                |   169 +-
 res/res_pjsip_transport_websocket.c                |   106 +-
 res/res_pjsip_xpidf_body_generator.c               |     9 +-
 res/res_pktccops.c                                 |     8 +-
 res/res_realtime.c                                 |     2 +-
 res/res_rtp_asterisk.c                             |   357 +-
 res/res_rtp_multicast.c                            |     2 +-
 res/res_security_log.c                             |     6 +-
 res/res_smdi.c                                     |     6 +-
 res/res_snmp.c                                     |     2 +-
 res/res_sorcery_astdb.c                            |     3 +-
 res/res_sorcery_config.c                           |    73 +-
 res/res_sorcery_memory.c                           |     2 +-
 res/res_sorcery_memory_cache.c                     |  3509 +++++
 res/res_sorcery_realtime.c                         |    20 +-
 res/res_speech.c                                   |     2 +-
 res/res_srtp.c                                     |     2 +-
 res/res_stasis.c                                   |    86 +-
 res/res_stasis_answer.c                            |     2 +-
 res/res_stasis_device_state.c                      |    58 +-
 res/res_stasis_mailbox.c                           |     2 +-
 res/res_stasis_playback.c                          |    10 +-
 res/res_stasis_recording.c                         |    12 +-
 res/res_stasis_snoop.c                             |    26 +-
 res/res_stasis_test.c                              |     2 +-
 res/res_statsd.c                                   |    90 +-
 res/res_statsd.exports.in                          |     1 +
 res/res_stun_monitor.c                             |     2 +-
 res/res_timing_dahdi.c                             |     2 +-
 res/res_timing_kqueue.c                            |   317 +-
 res/res_timing_pthread.c                           |     5 +-
 res/res_timing_timerfd.c                           |     5 +-
 res/res_xmpp.c                                     |    24 +-
 res/snmp/agent.c                                   |    12 +-
 res/stasis/app.c                                   |   379 +-
 res/stasis/app.h                                   |    15 +-
 res/stasis/command.c                               |     2 +-
 res/stasis/control.c                               |    38 +-
 res/stasis/messaging.c                             |    46 +-
 res/stasis/stasis_bridge.c                         |    62 +-
 res/stasis_recording/stored.c                      |     4 +-
 rest-api-templates/api.wiki.mustache               |    20 +-
 rest-api-templates/ari.make.mustache               |     1 +
 rest-api-templates/ari_model_validators.c.mustache |     6 +-
 rest-api-templates/ari_resource.c.mustache         |     2 +-
 rest-api-templates/ari_resource.h.mustache         |    19 +-
 rest-api-templates/asterisk_processor.py           |     6 +
 rest-api-templates/param_parsing.mustache          |     2 +-
 rest-api-templates/res_ari_resource.c.mustache     |    73 +-
 rest-api-templates/swagger_model.py                |     6 +
 rest-api/api-docs/applications.json                |     4 +-
 rest-api/api-docs/asterisk.json                    |   437 +-
 rest-api/api-docs/bridges.json                     |     8 +-
 rest-api/api-docs/channels.json                    |   124 +-
 rest-api/api-docs/deviceStates.json                |     4 +-
 rest-api/api-docs/endpoints.json                   |     8 +-
 rest-api/api-docs/events.json                      |   140 +-
 rest-api/api-docs/mailboxes.json                   |     4 +-
 rest-api/api-docs/playbacks.json                   |     4 +-
 rest-api/api-docs/recordings.json                  |     4 +-
 rest-api/api-docs/sounds.json                      |     4 +-
 rest-api/resources.json                            |     4 +-
 sounds/Makefile                                    |     5 +-
 sounds/sounds.xml                                  |    27 +
 tests/test_abstract_jb.c                           |     2 +-
 tests/test_acl.c                                   |    47 +-
 tests/test_amihooks.c                              |     2 +-
 tests/test_aoc.c                                   |     2 +-
 tests/test_app.c                                   |     2 +-
 tests/test_ari.c                                   |     2 +-
 tests/test_ari_model.c                             |     2 +-
 tests/test_ast_format_str_reduce.c                 |     2 +-
 tests/test_astobj2.c                               |     4 +-
 tests/test_astobj2_thrash.c                        |     2 +-
 tests/test_callerid.c                              |     2 +-
 tests/test_cdr.c                                   |    48 +-
 tests/test_cel.c                                   |    52 +-
 tests/test_channel_feature_hooks.c                 |     6 +-
 tests/test_config.c                                |   176 +-
 tests/test_core_codec.c                            |     2 +-
 tests/test_core_format.c                           |   107 +-
 tests/test_devicestate.c                           |   434 +-
 tests/test_dlinklists.c                            |     2 +-
 tests/test_endpoints.c                             |     2 +-
 tests/test_event.c                                 |     2 +-
 tests/test_expr.c                                  |     4 +-
 tests/test_format_cache.c                          |     2 +-
 tests/test_format_cap.c                            |     8 +-
 tests/test_func_file.c                             |     8 +-
 tests/test_gosub.c                                 |    12 +-
 tests/test_hashtab_thrash.c                        |     2 +-
 tests/test_heap.c                                  |     2 +-
 tests/test_jitterbuf.c                             |     2 +-
 tests/test_json.c                                  |     2 +-
 tests/test_linkedlists.c                           |     2 +-
 tests/test_locale.c                                |     2 +-
 tests/test_logger.c                                |     2 +-
 tests/test_message.c                               |    45 +-
 tests/test_optional_api.c                          |     2 +-
 tests/test_pbx.c                                   |    11 +-
 tests/test_poll.c                                  |     4 +-
 tests/test_res_stasis.c                            |     2 +-
 tests/test_sched.c                                 |     4 +-
 tests/test_security_events.c                       |     2 +-
 tests/test_skel.c                                  |     2 +-
 tests/test_sorcery.c                               |   132 +-
 tests/test_sorcery_memory_cache_thrash.c           |   618 +
 tests/test_sorcery_realtime.c                      |    12 +-
 tests/test_stasis.c                                |     6 +-
 tests/test_stasis_channels.c                       |     4 +-
 tests/test_stasis_endpoints.c                      |     2 +-
 tests/test_stringfields.c                          |     2 +-
 tests/test_strings.c                               |    77 +-
 tests/test_substitution.c                          |     2 +-
 tests/test_threadpool.c                            |    97 +-
 tests/test_time.c                                  |     2 +-
 tests/test_utils.c                                 |     2 +-
 tests/test_vector.c                                |   517 +
 tests/test_voicemail_api.c                         |    14 +-
 tests/test_xml_escape.c                            |     2 +-
 utils/Makefile                                     |     3 +
 utils/ael_main.c                                   |     2 +-
 utils/astdb2sqlite3.c                              |     2 +-
 utils/astman.c                                     |     4 +-
 utils/check_expr.c                                 |     2 +-
 utils/clicompat.c                                  |     9 +-
 utils/conf2ael.c                                   |    15 +-
 utils/extconf.c                                    |   336 +-
 utils/smsq.c                                       |     8 +-
 821 files changed, 50357 insertions(+), 9505 deletions(-)

diff --git a/.gitreview b/.gitreview
new file mode 100644
index 0000000..f9ef050
--- /dev/null
+++ b/.gitreview
@@ -0,0 +1,4 @@
+[gerrit]
+host=gerrit.asterisk.org
+port=29418
+project=asterisk.git
diff --git a/.version b/.version
index 21b80e9..4ed0bdb 100644
--- a/.version
+++ b/.version
@@ -1 +1 @@
-13.1.1
+13.7.2
\ No newline at end of file
diff --git a/CHANGES b/CHANGES
index ae368f1..40dbfab 100644
--- a/CHANGES
+++ b/CHANGES
@@ -7,6 +7,319 @@
 === and the other UPGRADE files for older releases.
 ===
 ==============================================================================
+
+------------------------------------------------------------------------------
+--- Functionality changes from Asterisk 13.6.0 to Asterisk 13.7.0 ------------
+------------------------------------------------------------------------------
+
+Codecs
+------------------
+ * Added format attribute negotiation for the VP8 video codec. Format attribute
+   negotiation is provided by the res_format_attr_vp8 module.
+
+ConfBridge
+------------------
+ * A new "timeout" user profile option has been added. This configures the number
+   of seconds that a participant may stay in the ConfBridge after joining. When
+   the time expires, the user is ejected from the conference and CONFBRIDGE_RESULT
+   is set to "TIMEOUT" on the channel.
+
+chan_sip
+------------------
+ * The websockets_enabled option has been added to the general section of
+   sip.conf.  The option is enabled by default to match the previous behavior.
+   The option should be disabled when using res_pjsip_transport_websockets to
+   ensure chan_sip will not conflict with PJSIP websockets.
+
+Dialplan Functions
+------------------
+ * The HOLD_INTERCEPT dialplan function now actually exists in the source tree.
+   While support for the events was added in Asterisk 13.4.0, the function
+   accidentally never made it in. That function is now present, and will cause
+   the 'hold' raised by a channel to be intercepted and converted into an
+   event instead.
+
+res_pjsip_outbound_registration
+-------------------------------
+ * A new 'fatal_retry_interval' option has been added to outbound registration.
+   When set (default is zero), and upon receiving a failure response to an
+   outbound registration, registration is retried at the given interval up to
+   'max_retries'.
+
+ * If res_statsd is loaded and a StatsD server is configured, basic statistics
+   regarding the state of outbound registrations will now be emitted. This
+   includes:
+   - A GAUGE statistic for the overall number of outbound registrations, i.e.:
+       PJSIP.registrations.count
+   - A GAUGE statistic for the overall number of outbound registrations in a
+     particular state, e.g.:
+       PJSIP.registrations.state.Registered
+
+res_pjsip
+------------------
+ * The ability to use "like" has been added to the pjsip list and show
+   CLI commands.  For instance: CLI> pjsip list endpoints like abc
+
+ * If res_statsd is loaded and a StatsD server is configured, basic statistics
+   regarding the state of PJSIP contacts will now be emitted. This includes:
+   - A GAUGE statistic for the overall number of contacts in a particular
+     state, e.g.:
+       PJSIP.contacts.states.Reachable
+   - A TIMER statistic for the RTT time for each qualified contact, e.g.:
+       PJSIP.contacts.alice@@127.0.0.1:5061.rtt
+
+res_sorcery_memory_cache
+------------------------
+ * A new caching strategy, full_backend_cache, has been added which caches
+   all stored objects in the backend. When enabled all objects will be
+   expired or go stale according to the configuration. As well when enabled
+   all retrieval operations will be performed against the cache instead of
+   the backend.
+
+func_callerid
+-------------------
+ * CALLERID(pres) is now documented as a valid alternative to setting both
+   CALLERID(name-pres) and CALLERID(num-pres) at once.  Some channel drivers,
+   like chan_sip, don't make a distinction between the two: they take the
+   least public value from name-pres and num-pres.  By using CALLERID(pres)
+   for reading and writing, you touch the same combined value in the dialplan.
+   The same applies to CONNECTEDLINE(pres), REDIRECTING(orig-pres),
+   REDIRECTING(to-pres) and REDIRECTING(from-pres).
+
+res_endpoint_stats
+-------------------
+ * A new module that emits StatsD statistics regarding Asterisk endpoints.
+   This includes a total count of the number of endpoints, the count of the
+   number of endpoints in the technology agnostic state of the endpoint -
+   online or offline - as well as the number of channels associated with each
+   endpoint. These are recorded as three different GAUGE statistics:
+    - endpoints.count
+    - endpoints.state.{unknown|offline|online}
+    - endpoints.{tech}.{resource}.channels
+
+
+------------------------------------------------------------------------------
+--- Functionality changes from Asterisk 13.5.0 to Asterisk 13.6.0 ------------
+------------------------------------------------------------------------------
+
+Dialplan Functions
+------------------
+ * The CHANNEL function, when used on a PJSIP channel, now exposes a 'call-id'
+   extraction option when using with the 'pjsip' signalling option. It will
+   return the SIP Call-ID associated with the INVITE request that established
+   the PJSIP channel.
+
+ARI
+------------------
+ * Two new endpoint related events are now available: PeerStatusChange and
+   ContactStatusChange. In particular, these events are useful when subscribing
+   to all event sources, as they provide additional endpoint related
+   information beyond the addition/removal of channels from an endpoint.
+   The ARI version has been bumped to 1.9.0 as a result.
+
+ * Added the ability to subscribe to all ARI events in Asterisk, regardless
+   of whether the application 'controls' the resource. This is useful for
+   scenarios where an ARI application merely wants to observe the system,
+   as opposed to control it. There are two ways to accomplish this:
+   (1) Via the WebSocket connection URI. A new query paramter, 'subscribeAll',
+       has been added that, when present and True, will subscribe all
+       specified applications to all ARI event sources in Asterisk.
+   (2) Via the applications resource. An ARI client can, at any time, subscribe
+       to all resources in an event source merely by not providing an explicit
+       resource. For example, subscribing to an event source of 'channels:'
+       as opposed to 'channels:12345' will subscribe the application to all
+       channels.
+
+------------------------------------------------------------------------------
+--- Functionality changes from Asterisk 13.4.0 to Asterisk 13.5.0 ------------
+------------------------------------------------------------------------------
+
+AMI
+------------------
+ * A new ContactStatus event has been added that reflects res_pjsip contact
+   lifecycle changes:  Created, Removed, Reachable, Unreachable, Unknown.
+
+ * Added the Linkedid header to the common channel headers listed for each
+   channel in AMI events.
+
+ARI
+------------------
+ * A new feature has been added that enables the retrieval of modules and
+   module information through an HTTP request. Information on a single module
+   can be also be retrieved. Individual modules can be loaded to Asterisk, as
+   well as unloaded and reloaded.
+
+* A new resource has been added to the 'asterisk' resource, 'config/dynamic'.
+   This resource allows for push configuration of sorcery derived objects
+   within Asterisk. The resource supports creation, retrieval, updating, and
+   deletion. Sorcery derived objects that are manipulated by this resource
+   must have a sorcery wizard that supports the desired operations.
+
+ * A new feature has been added that allows for the rotation of log channels
+   through HTTP requests.
+
+
+res_pjsip
+------------------
+* A new 'g726_non_standard' endpoint option has been added that, when set to
+  'yes' and g.726 audio is negotiated, forces the codec to be treated as if it
+  is AAL2 packed on the channel.
+
+* A new 'rtp_keepalive' endpoint option has been added. This option specifies
+  an interval, in seconds, at which we will send RTP comfort noise packets to
+  the endpoint. This functions identically to chan_sip's "rtpkeepalive" option.
+
+* New 'rtp_timeout' and 'rtp_timeout_hold' endpoint options have been added.
+  These options specify the amount of time, in seconds, that Asterisk will wait
+  before terminating the call due to lack of received RTP. These are identical
+  to chan_sip's rtptimeout and rtpholdtimeout options.
+
+------------------------------------------------------------------------------
+--- Functionality changes from Asterisk 13.3.0 to Asterisk 13.4.0 ------------
+------------------------------------------------------------------------------
+
+chan_pjsip
+------------------
+ * New 'rpid_immediate' option to control if connected line update information
+   goes to the caller immediately or waits for another reason to send the
+   connected line information update.  See the online option documentation for
+   more information.  Defaults to 'no' as setting it to 'yes' can result in
+   many unnecessary messages being sent to the caller.
+
+ * The configuration setting 'progressinband' now defaults to 'no', which
+   matches the actual behavior of previous versions.
+
+ * New 'line' and 'endpoint' options added on outbound registrations. This allows some
+   identifying information to be added to the Contact of the outbound registration.
+   If this information is present on messages received from the remote server
+   the message will automatically be associated with the configured endpoint on the
+   outbound registration.
+
+res_pjsip
+------------------
+ * A new CLI command has been added: "pjsip show settings", which shows
+   both the global and system configuration settings.
+
+ * A new global option has been added: "max_initial_qualify_time", which
+   sets the maximum amount of time from startup that qualifies should be
+   attempted on all contacts.
+
+* A new aor option has been added: "qualify_timeout", which sets the timeout
+   in seconds for a qualify.  The default is 3 seconds.  This overrides the
+   hard coded 32 seconds in pjproject.
+
+ * Endpoint status will now change to "Unreachable" when all contacts are
+   unavailable.  When any contact becomes available, the endpoint will status
+   will change back to "Reachable".
+
+res_ari_channels
+------------------
+ * Two new events, 'ChannelHold' and 'ChannelUnhold', have been added to the
+   events data model. These events are raised when a channel indicates a hold
+   or unhold, respectively.
+
+func_holdintercept
+------------------
+ * A new dialplan function, HOLD_INTERCEPT, has been added. This function, when
+   placed on a channel, intercepts hold/unhold indications signalled by the
+   channel and prevents them from moving on to other channels in a bridge with
+   the hold initiator. Instead, AMI or ARI events are raised indicating that
+   the channel wanted to place someone on hold. This allows external
+   applications to implement their own custom hold/unhold logic.
+
+------------------------------------------------------------------------------
+--- Functionality changes from Asterisk 13.2.0 to Asterisk 13.3.0 ------------
+------------------------------------------------------------------------------
+
+chan_pjsip/app_transfer
+------------------
+ * The Transfer application, when used with chan_pjsip, now supports using
+   a PJSIP endpoint as the transfer destination. This is in addition to
+   explicitly specifying a SIP URI to transfer to.
+
+res_ari_channels
+------------------
+ * The ARI /channels resource now supports a new operation, 'redirect'. The
+   redirect operation will perform a technology and state specific redirection
+   on the channel to a specified endpoint or destination. In the case of SIP
+   technologies, this is either a 302 Redirect response to an on-going INVITE
+   dialog or a SIP REFER request.
+
+res_pjsip
+------------------
+ * A new 'endpoint_identifier_order' option has been added that allows one to
+   set the order by which endpoint identifiers are processed and checked. This
+   option is specified under the 'global' type configuration section.
+
+------------------------------------------------------------------------------
+--- Functionality changes from Asterisk 13.1.0 to Asterisk 13.2.0 ------------
+------------------------------------------------------------------------------
+
+res_pjsip_outbound_registration
+------------------
+ * The 'pjsip send unregister' command now stops further registrations.
+
+ * A new command 'pjsip send register' has been added which allows you to
+   start or restart periodic registration.  It can be used after a
+   'send unregister' or after a 401 permanent error.
+
+chan_pjsip
+------------------
+ * New 'user_eq_phone' endpoint setting. This adds a 'user=phone' parameter
+   to the request URI and From URI if the user is determined to be a phone number.
+
+ * New global setting 'keep_alive_interval'. Setting this to a non-zero value
+   will cause keep alive messages to be sent periodically over connection
+   oriented transports.
+
+ * New 'PJSIP_AOR' and 'PJSIP_CONTACT' dialplan functions have been added which
+   allow examining PJSIP AORs or contacts from the dialplan.
+
+res_pjsip_config_wizard
+------------------
+ * This is a new module that adds streamlined configuration capability for
+   chan_pjsip.  It's targetted at users who have lots of basic configuration
+   scenarios like 'phone' or 'agent' or 'trunk'.  Additional information
+   can be found in the sample configuration file at
+   config/samples/pjsip_wizard.conf.sample.
+
+res_fax
+-----------
+ * The T.38 negotiation timeout was previously hard coded at 5000 milliseconds
+   and is now configurable via the 't38timeout' configuration option in
+   res_fax.conf and via the fax options dialplan function 'FAXOPT(t38timeout)'.
+   The default remains at 5000 milliseconds.
+
+PJSIP Transports
+----------
+ * The ca_list_path transport parameter has been added for TLS transports. This
+   option behaves similarly to the old sip.conf option "tlscapath". In order to
+   use this, you must be using PJProject version 2.4 or higher.
+
+ARI
+------------------
+ * The Originate operation now takes in an originator channel. The linked ID of
+   this originator channel is applied to the newly originated outgoing channel.
+   If using CEL this allows an association to be established between the two so
+   it can be recognized that the originator is dialing the originated channel.
+
+ * "language" (the default spoken language for the channel) is now included in
+   the standard channel state output for suitable events.
+
+ * The POST channels/{id} operation and the POST channels/{id}/continue operation
+   now have a new "label" parameter. This allows for origination or continuation
+   to a labeled priority in the dialplan instead of requiring a specific priority
+   number. The ARI version has been bumped to 1.7.0 as a result.
+
+AMI
+------------------
+ * "Language" (the default spoken language for the channel) is now included in
+   the standard channel state output for suitable events.
+
+ * AMI actions that return a list of events have been made to return consistent
+   headers for the action response event starting the list and the list complete
+   event.  The AMI version has been bumped to 2.7.0 as a result.
+
 ------------------------------------------------------------------------------
 --- Functionality changes from Asterisk 13.0.0 to Asterisk 13.1.0 ------------
 ------------------------------------------------------------------------------
@@ -95,7 +408,6 @@ included in Asterisk 13.
 Finally, all users upgrading to Asterisk 13 should read the UPGRADE.txt file
 delivered with this release.
 
-
 Build System
 ------------------
  * Sample config files have been moved from configs/ to a sub-folder of that
diff --git a/ChangeLog b/ChangeLog
index 287d52e..43b10bc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,37 +1,13615 @@
-2015-01-28  Asterisk Development Team <asteriskteam at digium.com>
+2016-02-05 20:32 +0000  Asterisk Development Team <asteriskteam at digium.com>
 
-	* Asterisk 13.1.1 Released.
+	* asterisk 13.7.2 Released.
 
-	* Mitigate possible HTTP injection attacks using CURL() function in
-	  Asterisk.
+2016-02-05 14:32 +0000 [0ba44bd6f3]  Mark Michelson <mmichelson at lunkwill.digium.internal>
 
-	  CVE-2014-8150 disclosed a vulnerability in libcURL where HTTP request
-	  injection can be performed given properly-crafted URLs.
+	* Release summaries: Remove previous versions
 
-	  Since Asterisk makes use of libcURL, and it is possible that users of
-	  Asterisk may get cURL URLs from user input or remote sources, we have
-	  made a patch to Asterisk to prevent such HTTP injection attacks from
-	  originating from Asterisk.
+2016-02-05 14:32 +0000 [b750dfe202]  Mark Michelson <mmichelson at lunkwill>
 
-	  ASTERISK-24676 #close
-	  Reported by: Matt Jordan, Olle Johansson
+	* .version: Update for 13.7.2
 
-	  Review: https://reviewboard.asterisk.org/r/4364
+2016-02-05 14:32 +0000 [c1b94ffe78]  Mark Michelson <mmichelson at lunkwill>
 
-	  AST-2015-002
+	* .lastclean: Update for 13.7.2
 
-	* Fix file descriptor leak in RTP code.
+2016-02-05 14:32 +0000 [932ed1ab5b]  Mark Michelson <mmichelson at lunkwill>
 
-	  SIP requests that offered codecs incompatible with configured values
-	  could result in the allocation of RTP and RTCP ports that would not
-	  get reclaimed later.
+	* realtime: Add database scripts for 13.7.2
 
-	  ASTERISK-24666 #close
-	  Reported by Y Ateya
+2016-02-02 10:52 +0000 [8de94229ba]  Alexei Gradinari License #5691
 
-	  Review: https://reviewboard.asterisk.org/r/4323
+	* res_sorcery_realtime: Fix regex regression.
 
-	  AST-2015-001
+	  A regression was introduced where searching for realtime PJSIP objects
+	  by regex by starting the regex with a leading "^" would cause no items
+	  to be returned.
+
+	  This was due to a change which attempted to drop the requirement for a
+	  leading "^" to be present due to how some CLI commands formulate their
+	  regexes. However, the change, rather than simply eliminating the
+	  requirement, caused any regexes that did begin with "^" to end up not
+	  returning the expected results.
+
+	  This change fixes the problem by inspecting the regex and formulating
+	  the realtime query differently depending on if it begins with "^".
+
+	  ASTERISK-25702 #close
+	  Reported by Nic Colledge
+
+	  Patches:
+	      realtime_retrieve_regex.patch submitted by Alexei Gradinari License #5691
+
+	  Change-Id: I055df608a6e6a10732044fa737a9fe8dca602693
+	  (cherry picked from commit 32fc784284b570a05841d95c6d9a373b4bf3a35d)
+
+2016-02-04 16:17 +0000 [a56f55d566]  Mark Michelson <mmichelson at digium.com>
+
+	* Check for OpenSSL defines before trying to use them.
+
+	  The SSL_OP_NO_TLSv1_1 and SSL_OP_NO_TLSv1_2 defines did not exist prior
+	  to OpenSSL version 1.0.1. A recent commit attempts to, by default, set
+	  these options, which can cause problems on systems with older OpenSSL
+	  installations.
+
+	  This commit adds a configure script check for those defines and will not
+	  attempt to make use of those if they do not exist. We will print a
+	  warning urging the user to upgrade their OpenSSL installation if those
+	  defines are not present.
+
+	  Change-Id: I6a2eb9a43fd0738b404d8f6f2cf4b5c22d9d752d
+
+2016-02-03 21:33 +0000  Asterisk Development Team <asteriskteam at digium.com>
+
+	* asterisk 13.7.1 Released.
+
+2016-02-03 15:33 +0000 [d99d10bd4c]  Kevin Harwell <kharwell at lunkwill.digium.internal>
+
+	* Release summaries: Remove previous versions
+
+2016-02-03 15:33 +0000 [e937a6db11]  Kevin Harwell <kharwell at lunkwill>
+
+	* .version: Update for 13.7.1
+
+2016-02-03 15:33 +0000 [30a6826f1e]  Kevin Harwell <kharwell at lunkwill>
+
+	* .lastclean: Update for 13.7.1
+
+2016-02-03 15:33 +0000 [1c79fa7eb8]  Kevin Harwell <kharwell at lunkwill>
+
+	* realtime: Add database scripts for 13.7.1
+
+2016-02-03 12:05 +0000 [1e7854dfa2]  Joshua Colp <jcolp at digium.com>
+
+	* AST-2016-001 http: Provide greater control of TLS and set modern defaults.
+
+	  This change exposes the configuration of various aspects of the TLS
+	  support and sets the default to the modern standards.
+
+	  The TLS cipher is now set to the best values according to the
+	  Mozilla OpSec team, different TLS versions can now be disabled, and
+	  the cipher order can be forced to be that of the server instead of
+	  the client.
+
+	  ASTERISK-24972 #close
+
+	  Change-Id: I0a10f2883f7559af5e48dee0901251dbf30d45b8
+2015-12-07 12:46 +0000 [b0646ff0da]  Richard Mudgett <rmudgett at digium.com>
+
+	* AST-2016-003 udptl.c: Fix uninitialized values.
+
+	  Sending UDPTL packets to Asterisk with the right amount of missing
+	  sequence numbers and enough redundant 0-length IFP packets, can make
+	  Asterisk crash.
+
+	  ASTERISK-25603 #close
+	  Reported by: Walter Doekes
+
+	  ASTERISK-25742 #close
+	  Reported by: Torrey Searle
+
+	  Change-Id: I97df8375041be986f3f266ac1946a538023a5255
+2015-09-28 17:07 +0000 [f08083f0f0]  Richard Mudgett <rmudgett at digium.com>
+
+	* AST-2016-002 chan_sip.c: Fix retransmission timeout integer overflow.
+
+	  Setting the sip.conf timert1 value to a value higher than 1245 can cause
+	  an integer overflow and result in large retransmit timeout times.  These
+	  large timeout times hold system file descriptors hostage and can cause the
+	  system to run out of file descriptors.
+
+	  NOTE: The default sip.conf timert1 value is 500 which does not expose the
+	  vulnerability.
+
+	  * The overflow is now detected and the previous timeout time is
+	  calculated.
+
+	  ASTERISK-25397 #close
+	  Reported by: Alexander Traud
+
+	  Change-Id: Ia7231f2f415af1cbf90b923e001b9219cff46290
+2016-01-15 19:01 +0000  Asterisk Development Team <asteriskteam at digium.com>
+
+	* asterisk 13.7.0 Released.
+
+2016-01-15 13:01 +0000 [8baf741e38]  Kevin Harwell <kharwell at lunkwill>
+
+	* Release summaries: Add summaries for 13.7.0
+
+2016-01-15 12:57 +0000 [e42707cf4a]  Kevin Harwell <kharwell at lunkwill.digium.internal>
+
+	* Release summaries: Remove previous versions
+
+2016-01-15 12:57 +0000 [e66f043868]  Kevin Harwell <kharwell at lunkwill>
+
+	* .version: Update for 13.7.0
+
+2016-01-15 12:57 +0000 [c3f6147748]  Kevin Harwell <kharwell at lunkwill>
+
+	* .lastclean: Update for 13.7.0
+
+2016-01-15 12:57 +0000 [3086c36c8d]  Kevin Harwell <kharwell at lunkwill>
+
+	* realtime: Add database scripts for 13.7.0
+
+2016-01-12 20:02 +0000  Asterisk Development Team <asteriskteam at digium.com>
+
+	* asterisk 13.7.0-rc3 Released.
+
+2016-01-12 14:02 +0000 [a5cb9e3f1d]  Kevin Harwell <kharwell at lunkwill>
+
+	* Release summaries: Add summaries for 13.7.0-rc3
+
+2016-01-12 14:02 +0000 [8043875fa1]  Kevin Harwell <kharwell at lunkwill.digium.internal>
+
+	* Release summaries: Remove previous versions
+
+2016-01-12 14:02 +0000 [a0e55b01c4]  Kevin Harwell <kharwell at lunkwill>
+
+	* .version: Update for 13.7.0-rc3
+
+2016-01-12 14:02 +0000 [ae3b9d0958]  Kevin Harwell <kharwell at lunkwill>
+
+	* .lastclean: Update for 13.7.0-rc3
+
+2016-01-12 14:02 +0000 [ebc1646def]  Kevin Harwell <kharwell at lunkwill>
+
+	* realtime: Add database scripts for 13.7.0-rc3
+
+2016-01-12 10:36 +0000 [6c5fbd08b8]  Mark Michelson <mmichelson at digium.com>
+
+	* res_sorcery_realtime: Remove leading ^ requirement.
+
+	  res_sorcery_realtime's search-by-regex callback performed a check to
+	  ensure that the passed-in regex began with a caret (^). If it did not,
+	  then no results would be returned.
+
+	  This callback only started to become used when "like" support was added
+	  to PJSIP CLI commands. The CLI command for listing objects would pass an
+	  empty regex ("") to the sorcery backend if no "like" statement was
+	  present. For most sorcery backends, this resulted in returning all
+	  objects. However, for realtime, this resulted in returning no objects.
+
+	  This commit seeks to fix the regression by removing the requirement from
+	  res_sorcery_realtime for the passed-in-regex to begin with a caret.
+
+	  ASTERISK-25689 #close
+	  Reported by Marcelo Terres
+
+	  Change-Id: I22b4dc5d7f3f11bb29ac2e42ef94682e9bab3b20
+
+2016-01-08 15:22 +0000 [20b22635dc]  Kevin Harwell <kharwell at digium.com>
+
+	* pbx: Deadlock between contexts container and context_merge locks
+
+	  Recent changes (ASTERISK-25394 commit 2bd27d12223fe33b58c453965ed5c6ed3af7c4f5)
+	  introduced the possibility of a deadlock. Due to the mentioned modifications
+	  ast_change_hints now needs to keep both merge/delete and state callbacks from
+	  occurring while it executes. Unfortunately, sometimes ast_change_hints can be
+	  called with the contexts container locked. When this happens it's possible for
+	  another thread to grab the context_merge_lock before the thread calling into
+	  ast_change_hints does and then try to obtain the contexts container lock. This
+	  of course causes a deadlock between the two threads. The thread calling into
+	  ast_change_hints waits for the other thread to release context_merge_lock and
+	  the other thread is waiting on that one to release the contexts container lock.
+
+	  Unfortunately, there is not a great way to fix this problem. When hints change,
+	  the subsequent state callbacks cannot run at the same time as a merge/delete,
+	  nor when the usual state callbacks do. This patch alleviates the problem by
+	  having those particular callbacks (the ones run after a hint change) occur in a
+	  serialized task. By moving the context_merge_lock to a task it can now safely be
+	  attempted or held without a deadlock occurring.
+
+	  ASTERISK-25640 #close
+	  Reported by: Krzysztof Trempala
+
+	  Change-Id: If2210ea241afd1585dc2594c16faff84579bf302
+
+2016-01-08 11:49 +0000 [21a9bc1b81]  Joshua Colp <jcolp at digium.com>
+
+	* res_rtp_asterisk: Revert DTLS negotiation changes.
+
+	  Due to locking issues within pjnath these changes are being
+	  reverted until pjnath can be changed.
+
+	  ASTERISK-25645
+
+	  Revert "res_rtp_asterisk.c: Fix DTLS negotiation delays."
+
+	  This reverts commit 24ae124e4f7310cfa64c187b944b2ffc060da28d.
+
+	  Change-Id: I2986cfb2c43dc14455c1bcaf92c3804f9da49705
+
+	  Revert "res_rtp_asterisk: Resolve further timing issues with DTLS negotiation"
+
+	  This reverts commit 965a0eee46d24321f74c244e23c5a5f45e67e12b.
+
+	  Change-Id: Ie68fafde27dad4b03cb7a1e27ce2a8502c3f7bbe
+
+2016-01-07 09:39 +0000 [0766192ee4]  Corey Farrell <git at cfware.com>
+
+	* ast_format_cap_append_by_type: Resolve codec reference leak.
+
+	  This resolves a reference leak caused by ASTERISK-25535.  The pointer
+	  returned by ast_format_get_codec is saved so it can be released.
+
+	  ASTERISK-25664 #close
+
+	  Change-Id: If9941b1bf4320b2c59056546d6bce9422726d1ec
+
+2015-12-18 18:18 +0000  Asterisk Development Team <asteriskteam at digium.com>
+
+	* asterisk 13.7.0-rc2 Released.
+
+2015-12-18 12:18 +0000 [a93a5387d4]  Kevin Harwell <kharwell at lunkwill>
+
+	* Release summaries: Add summaries for 13.7.0-rc2
+
+2015-12-18 12:18 +0000 [8e201b742a]  Kevin Harwell <kharwell at lunkwill.digium.internal>
+
+	* Release summaries: Remove previous versions
+
+2015-12-18 12:18 +0000 [5a164c70f2]  Kevin Harwell <kharwell at lunkwill>
+
+	* .version: Update for 13.7.0-rc2
+
+2015-12-18 12:18 +0000 [e039eca0a7]  Kevin Harwell <kharwell at lunkwill>
+
+	* .lastclean: Update for 13.7.0-rc2
+
+2015-12-18 12:18 +0000 [bfe2eb8751]  Kevin Harwell <kharwell at lunkwill>
+
+	* realtime: Add database scripts for 13.7.0-rc2
+
+2015-12-16 11:25 +0000 [805297783d]  Mark Michelson <mmichelson at digium.com>
+
+	* Alembic: Add PJSIP global keep_alive_interval.
+
+	  The keep_alive_interval option was added about a year ago, but no
+	  alembic revision was created to add the appropriate column to the
+	  database.
+
+	  This commit fixes the problem and adds the column. This was discovered
+	  by running the testsuite with automatic conversion to realtime enabled.
+
+	  Change-Id: If3ef92a7c4f4844d08f8aae170d2178aec5c4c1a
+
+2015-12-16 11:28 +0000 [63df9bb560]  Mark Michelson <mmichelson at digium.com>
+
+	* Alembic: Increase column size of PJSIP AOR "contact".
+
+	  When running the PJSIP AMI "show_endpoint" test with automatic
+	  conversion to realtime, the test would fail. This was because the AOR
+	  "contact" column was sized at 40, and the configured contact was larger
+	  than that.
+
+	  This commit increases the size of the contact column to 255 characters.
+
+	  Change-Id: Ia65bc7fd37699b7c0eaef9629a1a31eab9a24ba1
+
+2015-12-14 12:04 +0000 [acd19d5f1f]  Joshua Colp <jcolp at digium.com>
+
+	* json: Audit ast_json_* usage for thread safety.
+
+	  The JSON library Asterisk uses, jansson, is not thread
+	  safe for us in a few ways. To help with this wrappers for JSON
+	  object reference count increasing and decreasing were added
+	  which use a global lock to ensure they don't clobber over
+	  each other. This does not extend to reference count manipulation
+	  within the jansson library itself. This means you can't safely
+	  use the object borrowing specifier (O) in ast_json_pack and
+	  you can't share JSON instances between objects.
+
+	  This change removes uses of the O specifier and replaces them
+	  with the o specifier and an explicit ast_json_ref. Some cases
+	  of instance sharing have also been removed.
+
+	  ASTERISK-25601 #close
+
+	  Change-Id: I06550d8b0cc1bfeb56cab580a4e608ae4f1ec7d1
+
+2015-12-05 10:01 +0000 [11ff4e6b3f]  Joshua Colp <jcolp at digium.com>
+
+	* res_sorcery_memory_cache: Add support for a full backend cache.
+
+	  This change introduces the configuration option 'full_backend_cache'
+	  which changes the cache to be a full mirror of the backend instead
+	  of a per-object cache. This allows all sorcery retrieval operations
+	  to be carried out against it and is useful for object types which
+	  are used in a "retrieve all" or "retrieve some" pattern.
+
+	  ASTERISK-25625 #close
+
+	  Change-Id: Ie2993487e9c19de563413ad5561c7403b48caab5
+	  (cherry picked from commit 59d5bb0613810418f2a618b9a6dee5bcfd45767e)
+
+2015-12-17 10:25 +0000 [9bc1e49325]  Joshua Colp <jcolp at digium.com>
+
+	* rtp_engine: Ignore empty filenames in DTLS configuration.
+
+	  When applying an empty DTLS configuration the filenames in the
+	  configuration will be empty. This is actually valid to do and
+	  each filename should simply be ignored.
+
+	  Change-Id: Ib761dc235638a3fb701df337952f831fc3e69539
+
+2015-12-17 08:10 +0000 [c78eb1e82b]  Joshua Colp <jcolp at digium.com>
+
+	* chan_sip: Enable WebSocket support by default.
+
+	  Per the documentation the WebSocket support in chan_sip is
+	  supposed to be enabled by default but is not. This change
+	  corrects that.
+
+	  Change-Id: Icb02bbcad47b11a795c14ce20a9bf29649a54423
+
+2015-12-15 18:01 +0000  Asterisk Development Team <asteriskteam at digium.com>
+
+	* asterisk 13.7.0-rc1 Released.
+
+2015-12-15 11:57 +0000 [0370acecfc]  Kevin Harwell <kharwell at lunkwill>
+
+	* Release summaries: Add summaries for 13.7.0-rc1
+
+2015-12-15 11:54 +0000 [d1bb33fe0b]  Kevin Harwell <kharwell at lunkwill>
+
+	* .version: Update for 13.7.0-rc1
+
+2015-12-15 11:54 +0000 [d06a65de01]  Kevin Harwell <kharwell at lunkwill>
+
+	* .lastclean: Update for 13.7.0-rc1
+
+2015-12-15 11:54 +0000 [fb37b44660]  Kevin Harwell <kharwell at lunkwill>
+
+	* realtime: Add database scripts for 13.7.0-rc1
+
+2015-12-15 11:48 +0000 [20b7164b8c]  Kevin Harwell <kharwell at lunkwill>
+
+	* .version: Update for 13.7.0-rc1
+
+2015-12-15 11:48 +0000 [6cbf2414c3]  Kevin Harwell <kharwell at lunkwill>
+
+	* .lastclean: Update for 13.7.0-rc1
+
+2015-12-15 11:48 +0000 [ba1794464d]  Kevin Harwell <kharwell at lunkwill>
+
+	* realtime: Add database scripts for 13.7.0-rc1
+
+2015-12-15 11:39 +0000 [b3e9753a23]  Kevin Harwell <kharwell at lunkwill>
+
+	* .version: Update for 13.7.0-rc1
+
+2015-12-15 11:39 +0000 [b0df64b5f0]  Kevin Harwell <kharwell at lunkwill>
+
+	* .lastclean: Update for 13.7.0-rc1
+
+2015-12-15 11:39 +0000 [ce9a59faf6]  Kevin Harwell <kharwell at lunkwill>
+
+	* realtime: Add database scripts for 13.7.0-rc1
+
+2015-12-15 11:28 +0000 [2e26bef5bb]  Kevin Harwell <kharwell at lunkwill>
+
+	* .version: Update for 13.7.0-rc1
+
+2015-12-15 11:28 +0000 [5e9b47516d]  Kevin Harwell <kharwell at lunkwill>
+
+	* .lastclean: Update for 13.7.0-rc1
+
+2015-12-15 11:28 +0000 [034112c574]  Kevin Harwell <kharwell at lunkwill>
+
+	* realtime: Add database scripts for 13.7.0-rc1
+
+2015-12-15 11:19 +0000 [d1f8ff1789]  Kevin Harwell <kharwell at lunkwill>
+
+	* .version: Update for 13.7.0-rc1
+
+2015-12-15 11:19 +0000 [9376488bef]  Kevin Harwell <kharwell at lunkwill>
+
+	* .lastclean: Update for 13.7.0-rc1
+
+2015-12-15 11:19 +0000 [a894c9e7a9]  Kevin Harwell <kharwell at lunkwill>
+
+	* realtime: Add database scripts for 13.7.0-rc1
+
+2015-12-15 11:12 +0000 [52afb0f112]  Kevin Harwell <kharwell at lunkwill>
+
+	* .version: Update for 13.7.0-rc1
+
+2015-12-15 11:12 +0000 [2de343eb85]  Kevin Harwell <kharwell at lunkwill>
+
+	* .lastclean: Update for 13.7.0-rc1
+
+2015-12-15 11:12 +0000 [184de2a160]  Kevin Harwell <kharwell at lunkwill>
+
+	* realtime: Add database scripts for 13.7.0-rc1
+
+2015-12-14 13:53 +0000 [24ae124e4f]  server-pandora <server-pandora at xencall.com>
+
+	* res_rtp_asterisk.c: Fix DTLS negotiation delays.
+
+	  - Trigger pending DTLS packets to send out, once the RTP instance's remote
+	    address is set.
+	  - Avoids locking the DTLS structure unnecessarily by only doing this if
+	    DTLS is passive.
+	  - Add DTLS locks around the structurally sensitive calls in the SSL
+	    portion of __rtp_recvfrom, since dtls_srtp_check_pending does not lock
+	    inside of itself, and we're dealing with the SSL BIO in at least two
+	    threads.
+
+	  WebRTC channels may receive a DTLS handshake before
+	  ast_rtp_remote_address_set is called, which causes there to be a pending
+	  response to send out.   Previous to 1ad827, this was handled by calling
+	  dtls_srtp_check_pending on receipt of any RTP packet - a STUN or RTP
+	  packet could trigger the pending handshake response.  Since that was
+	  rightfully removed, whenever the DTLS handshake is received before the
+	  remote address is set, we would have to wait until another SSL packet
+	  arrives.
+
+	  As of Chrome M47's optimizations to their handshake process, WebRTC
+	  conversations between Chrome M47+ and Asterisk, where Asterisk is passive,
+	  experience a 1 second delay without this patch, because the SSL handshake
+	  is received before ICE negotation stores the remote_address, and the next
+	  SSL packet isn't received until after a 1 second timeout in Chrome, which
+	  causes a new handshake request.
+
+	  ASTERISK-25614 #close
+
+	  Change-Id: I547f1be7e302dbf71f6553dd8cbc0657b1d0b908
+
+2015-12-14 15:25 +0000 [36097a185d]  Richard Mudgett <rmudgett at digium.com>
+
+	* Fix sscanf() format string type mismatch.
+
+	  ASTERISK-25615
+	  Reported by: George Joseph
+
+	  Change-Id: Ieff35307254ca193f3d473cff2e396ca57c7ce0b
+
+2015-12-13 13:13 +0000 [94f9927784]  Matt Jordan <mjordan at digium.com>
+
+	* main/utils: Don't emit an ERROR message if the read end of a pipe closes
+
+	  An ERROR or WARNING message should generally indicate that something has gone
+	  wrong in Asterisk. In the case of writing to a file descriptor, Asterisk is not
+	  in control of when the far end closes its reading on a file descriptor. If the
+	  far end does close the file descriptor in an unclean fashion, this isn't a bug
+	  or error in Asterisk, particularly when the situation can be gracefully
+	  handled in Asterisk.
+
+	  Currently, when this happens, a user would see the following somewhat cryptic
+	  ERROR message:
+
+	    "utils.c: write() returned error: Broken pipe"
+
+	  There's a few problems with this:
+	  (1) It doesn't provide any context, other than 'something broke a pipe'
+	  (2) As noted, it isn't actually an error in Asterisk
+	  (3) It can get rather spammy if the thing breaking the pipe occurs often, such
+	      as a FastAGI server
+	  (4) Spammy ERROR messages make Asterisk appear to be having issues, or can even
+	      mask legitimate issues
+
+	  This patch changes ast_carefulwrite to only log an ERROR if we actually had one
+	  that was reasonably under our control. For debugging purposes, we still emit
+	  a debug message if we detect that the far side has stopped reading.
+
+	  Change-Id: Ia503bb1efcec685fa6f3017bedf98061f8e1b566
+
+2015-12-12 11:08 +0000 [5b867fa904]  gtjoseph <george.joseph at fairview5.com>
+
+	* pjsip/config_transport: Check pjproject version at runtime for async ops
+
+	  pjproject < 2.5.0 will segfault on a tls transport if async_operations
+	  is greater than 1.  A runtime version check has been added to throw
+	  an error if the version is < 2.5.0 and async_operations > 1.
+
+	  To assist in the check, a new api "ast_compare_versions" was added
+	  to utils which compares 2 major.minor.patch.extra version strings.
+
+	  ASTERISK-25615 #close
+
+	  Change-Id: I8e88bb49cbcfbca88d9de705496d6f6a8c938a98
+	  Reported-by: George Joseph
+	  Tested-by: George Joseph
+
+2015-12-10 11:44 +0000 [14b41115e3]  Jonathan Rose <jrose at digium.com>
+
+	* chan_sip: Add TCP/TLS keepalive to TCP/TLS server
+
+	  Adds the TCP Keep Alive option to TCP and TLS server sockets. Previously
+	  this option was only being set on session sockets.
+	  http://www.tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO/
+	  According to the link above, the SO_KEEPALIVE option is useful for knowing
+	  when a TCP connected endpoint has severed communication without indicating
+	  it or has become unreachable for some reason. Without this patch, keep
+	  alive is not set on the socket listening for incoming TCP sessions and
+	  in Komatsu's report this resulted in the thread listening for TCP becoming
+	  stuck in a waiting state.
+
+	  ASTERISK-25364 #close
+	  Reported by: Hiroaki Komatsu
+
+	  Change-Id: I7ed7bcfa982b367dc64b4b73fbd962da49b9af36
+2015-12-09 09:48 +0000 [cd119ed4a2]  Tyler Cambron <tcambron at digium.com>
+
+	* res_chan_stats: Fix bug to send correct statistics to StatsD
+
+	  Fixed a bug that originally would show a negative number of
+	  active calls occuring in Asterisk. A gauge is persistent so
+	  incrementing and decrementing it results in a more consistent
+	  performance. Also changed to the call to StatsD to use
+	  ast_statsd_log_string() so that a "+" could be sent to StatsD.
+
+	  ASTERISK-25619 #close
+
+	  Change-Id: Iaaeff5c4c6a46535366b4d16ea0ed0ee75ab2ee7
+
+2015-12-07 13:07 +0000 [ddf4dddf4f]  Corey Farrell <git at cfware.com>
+
+	* app_meetme: Set default value for audio_buffers.
+
+	  The default value was never set for audio_buffers, causing bad
+	  audio quality.  This ensures the default is always set.
+
+	  ASTERISK-25569 #close
+
+	  Change-Id: I2d2ee3e644120b0f9f6ea6ab9286d7d590942a44
+2015-12-08 01:57 +0000 [142d4fefb8]  Filip Jenicek <phill at janevim.cz>
+
+	* chan_sip: Check sip_pvt pointer in ast_channel_get_t38_state(c)
+
+	  Asterisk may crash when calling ast_channel_get_t38_state(c)
+	  on a locked channel which is being hung up.
+
+	  ASTERISK-25609 #close
+
+	  Change-Id: Ifaa707c04b865a290ffab719bd2e5c48ff667c7b
+
+2015-12-08 17:49 +0000 [21962dad93]  gtjoseph <george.joseph at fairview5.com>
+
+	* res_pjsip:  Add existence and readablity checks for tls related files
+
+	  Both transport and endpoint now check for the existence and readability
+	  of tls certificate and key files before passing them on to pjproject.
+	  This will cause the object to not load rather than waiting for pjproject
+	  to discover that there's a problem when a session is attempted.
+
+	  NOTE: chan_sip also uses ast_rtp_dtls_cfg_parse but it's located
+	  in build_peer which is gigantic and I didn't want to disturb it.
+	  Error messages will emit but it won't interrupt chan_sip loading.
+
+	  ASTERISK-25618 #close
+
+	  Change-Id: Ie43f2c1d653ac1fda6a6f6faecb7c2ebadaf47c9
+	  Reported-by: George Joseph
+	  Tested-by: George Joseph
+
+2015-12-02 12:42 +0000 [28d9243079]  Eugene Voityuk <eugene at thirdlane.com>
+
+	* chan_sip.c: Start ICE negotiation when response is sent or received.
+
+	  The current logic for ICE negotiation starts it
+	  when receiving an SDP with ICE candidates. This is
+	  incorrect as ICE negotiation can only start when each 
+	  call party have at least one pair of local and remote 
+	  candidate. Starting ICE negotiation early would result 
+	  in negotiation failure and ultimately no audio.
+
+	  This change makes it so ICE negotiation is only started
+	  when a response with SDP is received or when a response
+	  with SDP is sent.
+
+	  ASTERISK-24146
+
+	  Change-Id: I55a632bde9e9827871b09141d82747e08379a8ca
+2015-12-08 11:03 +0000 [e03582a1c2]  gtjoseph <george.joseph at fairview5.com>
+
+	* res_pjsip/config_transport: Prevent async_operations > 1 when protocol = tls
+
+	  See ASTERISK-25615.
+	  If the transport protocol is tls and async_operations > 1, pjproject
+	  will segfault if more than one operation is attempted on the same socket.
+	  Until this is fixed upstream, a check has been added to throw an error
+	  if a tls transport config has async_operations set to > 1.
+
+	  ASTERISK-25615
+
+	  Change-Id: I76b9a5b2a5a0054fe71ca5851e635f2dca7685a6
+	  Reported-by: George Joseph
+	  Tested-by: George Joseph
+
+2015-12-08 08:39 +0000 [876600ce6e]  Alexander Traud <pabstraud at compuserve.com>
+
+	* codec_resample: Increase buffer for Opus Codec with FEC.
+
+	  ASTERISK-25599 #close
+
+	  Change-Id: Idbd187f711b2ec63dda949ca0f79aa0c1a0a0b6e
+
+2015-12-08 03:46 +0000 [69e3d40ad7]  Alexander Traud <pabstraud at compuserve.com>
+
+	* translate: Avoid a warning message when doing FEC within Opus Codec.
+
+	  ASTERISK-25616 #close
+
+	  Change-Id: Ibe729aaf2e6e25506cff247cec5149ec1e589319
+
+2015-12-04 15:36 +0000 [2b992014dc]  Richard Mudgett <rmudgett at digium.com>
+
+	* chan_sip: Fix crash involving the bogus peer during sip reload.
+
+	  A crash happens sometimes when performing a CLI "sip reload".  The bogus
+	  peer gets refreshed while it is in use by a new call which can cause the
+	  crash.
+
+	  * Protected the global bogus peer object with an ao2 global object
+	  container.
+
+	  ASTERISK-25610 #close
+
+	  Change-Id: I5b528c742195681abcf713c6e1011ea65354eeed
+
+2015-12-06 16:32 +0000 [529535f0c2]  Matt Jordan <mjordan at digium.com>
+
+	* Revert "bridges/bridge_t38: Add a bridging module for managing T.38 state"
+
+	  This reverts commit 6614babea27fbafbe11820ea03737dd5c4f9ecec.
+
+	  Unfortunately, using a bridge to manage T.38 state will cause severe deadlocks
+	  in core_unreal/chan_local. Local channels attempt to reach across both their
+	  peer and the peer's bridge to inspect T.38 state. Given the propensity of
+	  Local channel chains, managing the locking situation in such a scenario is
+	  practically infeasible.
+
+	  Change-Id: Ic687397ffea08dfb899345a443bd990ec3d0416a
+
+2015-12-04 16:23 +0000 [450579e908]  gtjoseph <george.joseph at fairview5.com>
+
+	* res_pjsip/contacts/statsd:  Make contact lifecycle events more consistent
+
+	  It will never be perfect or even pretty, mostly because of the differences
+	  between static and dynamic contacts.
+
+	  Created:
+
+	  Can't use the contact or contact_status alloc functions
+	  because the objects come and go regardless of the actual state.
+
+	  Can't use the contact_apply_handler, ast_sip_location_add_contact or
+	  a sorcery created handler because they only get called for dynamic
+	  contacts.  Similarly, permanent_uri_handler only gets called for
+	  static contacts.
+
+	  So, Matt had it right. :)  ast_res_pjsip_find_or_create_contact_status is
+	  the only place it can go and not have duplicated code.  Both
+	  permanent_uri_handler and contact_apply_handler call find_or_create.
+
+	  Removed:
+
+	  Can't use the destructors for the same reason as above.  The only
+	  place to put this is in persistent_endpoint_contact_deleted_observer
+	  which I believe is the "correct" place but even that will handle only
+	  dynamic contacts.  This doesn't called on shutdown however.  There is
+	  no hook to use for static contacts that may be removed because of a
+	  config change while asterisk is in operation.
+
+	  I moved the cleanup of contact_status from ast_sip_location_delete_contact
+	  to the handler as well.
+
+	  Status Change and RTT:
+
+	  Although they worked fine where they were (in update_contact_status) I
+	  moved them to persistent_endpoint_contact_status_observer to make it
+	  more consistent with removed.  There was logic there already to detect
+	  a state change.
+
+	  Finally, fixed a nit in permanent_uri_handler rmudgett reported
+	  eralier.
+
+	  ASTERISK-25608 #close
+
+	  Change-Id: I4b56e7dfc3be3baaaf6f1eac5b2068a0b79e357d
+	  Reported-by: George Joseph
+	  Tested-by: George Joseph
+
+2015-11-21 06:02 +0000 [5a18193dc0]  Alexander Traud <pabstraud at compuserve.com>
+
+	* res_format_attr_vp8: In SDP, forward max-fr and max-fs for video-codec VP8.
+
+	  ASTERISK-25584 #close
+
+	  Change-Id: Iae00071b4ff1ae76f24995aeac4d00284fd14f91
+
+2015-11-21 05:21 +0000 [3e2178c05e]  Alexander Traud <pabstraud at compuserve.com>
+
+	* res_format_attr_opus: Update to latest RFC 7587.
+
+	  Beside that, the format-attribute module sends only non-default values in the
+	  line fmtp, now. This avoids unnecessary overhead in SDP messages. Furthermore,
+	  previously the parameter stereo was not parsed when being the first parameter.
+
+	  ASTERISK-25583 #close
+
+	  Change-Id: Iae85ba3e5960bfd5d51cf65bcffad00dd4875a73
+2015-12-02 14:11 +0000 [072d94183c]  Jonathan Rose <jrose at digium.com>
+
+	* Fix crash in audiohook translate to slin
+
+	  This patch fixes a crash which would occur when an audiohook was
+	  applied to a channel using an audio codec that could not be translated
+	  to signed linear (such as when using pass-through codecs like OPUS or
+	  when the codec translator module for the format in use is not loaded).
+
+	  ASTERISK-25498 #close
+	  Reported by: Ben Langfeld
+
+	  Change-Id: Ib6ea7373fcc22e537cad373996136636201f4384
+
+2015-12-03 12:07 +0000 [9184fbeb34]  gtjoseph <george.joseph at fairview5.com>
+
+	* res_pjsip: Use a MD5 hash for static Contact IDs
+
+	  When 90d9a70789 was merged, it mostly tested dynamic contacts created as
+	  a result of registering a PJSIP endpoint. Contacts generated in this
+	  fashion typically have a long alphanumeric string as their object identifier,
+	  which maps reasonably well for StatsD. Unfortunately, this doesn't work in the
+	  general case. StatsD treats both '.' and ':' characters as special characters.
+	  In particular, having a ':' appear in the middle of a StatsD metric will
+	  result in the metric being rejected.
+
+	  This causes some obvious issues with SIP URIs.
+
+	  The StatsD API should not be responsible for escaping the metric name passed
+	  to it. The metric is treated as a single long string, and it would be
+	  challenging to know what to escape in the string passed to the function.
+	  Likewise, we don't want to escape the metric in PJSIP, as that involves
+	  overhead that is wasted when either res_statsd isn't loaded or enabled.
+
+	  This patch takes an alternative approach. The Contact ID has been changed
+	  to be "aor@@uri_hash" instead of "aor@@uri". This (a) won't contain any of the
+	  aforementioned special characters, (b) can be done on Contact creation,
+	  which has minimal impact on run-time performance, and (c) also conforms to an
+	  earlier commit that changed the ID for dynamic contacts.
+
+	  The downside of this is that StatsD users will have to map SHA1 hashes back to
+	  the Contacts that are emitting the statistics. To that end, the CLI commands
+	  have been updated to include the first 10 characters of the MD5 hash, which
+	  should be enough to match what is shown in Graphite (or some other StatsD
+	  backend).
+
+	  ASTERISK-25595 #close
+
+	  Change-Id: Ic674a3307280365b4a45864a3571c295b48a01e2
+	  Reported-by: Matt Jordan
+	  Tested-by: George Joseph
+
+2015-11-30 22:19 +0000 [ed9134282e]  gtjoseph <george.joseph at fairview5.com>
+
+	* res_pjsip:  Update logging to show contact->uri in messages
+
+	  An earlier commit changed the id of dynamic contacts to contain
+	  a hash instead of the uri.  This patch updates status change
+	  logging to show the aor/uri instead of the id.  This required
+	  adding the aor id to contact and contact_status and adding
+	  uri to contact_status.  The aor id gets added to contact and
+	  contact_status in their allocators and the uri gets added to
+	  contact_status in pjsip_options when the contact_status is
+	  created or updated.
+
+	  ASTERISK-25598 #close
+
+	  Reported-by: George Joseph
+	  Tested-by: George Joseph
+
+	  Change-Id: I56cbec1d2ddbe8461367dd8b6da8a6f47f6fe511
+
+2015-12-01 16:11 +0000 [eadad24b59]  Jonathan Rose <jrose at digium.com>
+
+	* Unset BRIDGEPEER when leaving a bridge
+
+	  Currently if a channel is transferred out of a bridge, the BRIDGEPEER
+	  variable (also BRIDGEPVTCALLID) remain set even once the channel is
+	  out of the bridge. This patch removes these variables when leaving
+	  the bridge.
+
+	  ASTERISK-25600 #close
+	  Reported by: Mark Michelson
+
+	  Change-Id: I753ead2fffbfc65427ed4e9244c7066610e546da
+
+2015-11-30 14:22 +0000 [bb0b60619d]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_sorcery_memory_cache.c: Fix off nominal ref leak.
+
+	  Change-Id: If83d63cf11cbc6df9b15251848b01feb570ade49
+
+2015-11-30 16:42 +0000 [e7c88e11aa]  Richard Mudgett <rmudgett at digium.com>
+
+	* sched.c: Make not return a sched id of 0.
+
+	  According to the API doxygen a sched ID of 0 is valid.  Unfortunately, 0
+	  was never returned historically and several users incorrectly coded usage
+	  of the returned sched ID assuming that 0 was invalid.
+
+	  ASTERISK-25476
+
+	  Change-Id: Ib19c7ebb44ec9fd393ef6646dea806d4f34e3a20
+
+2015-11-25 12:23 +0000 [4aed349a7b]  Richard Mudgett <rmudgett at digium.com>
+
+	* Audit improper usage of scheduler exposed by 5c713fdf18f. (v13 additions)
+
+	  chan_sip.c:
+	  * Initialize mwi subscription scheduler ids earlier because of ASTOBJ to
+	  ao2 conversion.
+
+	  * Initialize register scheduler ids earlier because of ASTOBJ to ao2
+	  conversion.
+
+	  chan_skinny.c:
+	  * Fix more scheduler usage for the valid 0 id value.
+
+	  ASTERISK-25476
+
+	  Change-Id: If9f0e5d99638b2f9d102d1ebc9c5a14b2d706e95
+
+2015-11-24 12:44 +0000 [6d9156d10f]  Richard Mudgett <rmudgett at digium.com>
+
+	* Audit improper usage of scheduler exposed by 5c713fdf18f.
+
+	  channels/chan_iax2.c:
+	  * Initialize struct chan_iax2_pvt scheduler ids earlier because of
+	  iax2_destroy_helper().
+
+	  channels/chan_sip.c:
+	  channels/sip/config_parser.c:
+	  * Fix initialization of scheduler id struct members.  Some off nominal
+	  paths had 0 as a scheduler id to be destroyed when it was never started.
+
+	  chan_skinny.c:
+	  * Fix some scheduler id comparisons that excluded the valid 0 id.
+
+	  channel.c:
+	  * Fix channel initialization of the video stream scheduler id.
+
+	  pbx_dundi.c:
+	  * Fix channel initialization of the packet retransmission scheduler id.
+
+	  ASTERISK-25476
+
+	  Change-Id: I07a3449f728f671d326a22fcbd071f150ba2e8c8
+
+2015-12-01 07:55 +0000 [b76c196e13]  Alexander Traud <pabstraud at compuserve.com>
+
+	* codec_resample: Increase buffer for Opus Codec.
+
+	  ASTERISK-25599 #close
+
+	  Change-Id: I1f88a88c59fb4e1e62bbdbb100c7152d48e73f10
+
+2015-11-28 08:46 +0000 [6614babea2]  Matt Jordan <mjordan at digium.com>
+
+	* bridges/bridge_t38: Add a bridging module for managing T.38 state
+
+	  When 4875e5ac32 was merged, it fixed several issues with a direct media bridge
+	  transitioning to handling a T.38 fax. However, it uncovered a race condition
+	  caused by the bridging core. When a channel involved in a T.38 fax leaves a
+	  bridge, the frame queued by the channel driver that should inform the far side
+	  that it is no longer in a T.38 fax may not make it across the bridge. The
+	  bridging framework is *extremely* aggressive in tearing down the bridge, and
+	  control frames that are currently in flight *may* get dropped.
+
+	  This patch adds a new module to the bridging framework, bridge_t38. This module
+	  maintains some notion of the T.38 state for the two channels in a bridge. When
+	  the bridge detects that it is being torn down or when one of the two channels
+	  leaves, it informs the respective channel(s) that they should stop faxing. This
+	  ensures that channels switch back to audio if they survive and are ejected out
+	  of a bridge while faxing.
+
+	  ASTERISK-25582
+
+	  Change-Id: If5b0bb478eb01c4607c9f4a7fc17c7957d260ea0
+
+2015-11-27 07:39 +0000 [3fcf160fae]  Niklas Larsson <niklas at tese.se>
+
+	* CHANGES: Fix a typo
+
+	  Change-Id: Iceb3d9bb78140c376174a7bee197dfcf8ef9cda7
+2015-11-25 15:26 +0000 [45efbf8503]  Kevin Harwell <kharwell at digium.com>
+
+	* fastagi: record file closed after sending result
+
+	  The fastagi record-file testsuite test sometimes fails reporting an empty
+	  recorded file. This was happening because Asterisk was sending the agi result
+	  notification prior to actually closing the file and the data, being buffered,
+	  had not been written to the file yet when the test attempts to check the file
+	  size.
+
+	  This patch makes it so the record file stream is closed prior to sending the
+	  agi result notification.
+
+	  ASTERISK-25593 #close
+
+	  Change-Id: I6b2b3be3ae37f7c7b18e672c419a89b3b8513cde
+
+2015-11-25 13:29 +0000 [b2787876d6]  Walter Doekes <walter+asterisk at wjd.nu>
+
+	* main: Slight refactor of main. Improve color situation.
+
+	  Several issues are addressed here:
+	  - main() is large, and half of it is only used if we're not rasterisk;
+	    fixed by spliting up the daemon part into a separate function.
+	  - Call ast_term_init from rasterisk as well.
+	  - Remove duplicate code reading/writing asterisk history file.
+	  - Attempt to tackle background color issues and color changes that
+	    occur. Tested by starting asterisk -c until the colors stopped
+	    changing at odd locations.
+
+	  ASTERISK-25585 #close
+
+	  Change-Id: Ib641a0964c59ef9fe6f59efa8ccb481a9580c52f
+
+2015-11-24 13:54 +0000 [59881fbb99]  David M. Lee <dlee at respoke.io>
+
+	* Fixed some typos
+
+	  Fixes some minor typos in the CHANGES file, plus an embarrasing typo in
+	  the StatsD API.
+
+	  Change-Id: I9ca4858c64a4a07d2643b81baa64baebb27a4eb7
+
+2015-11-24 13:07 +0000 [b75f587d15]  Corey Farrell <git at cfware.com>
+
+	* res_pjsip_notify: Fix CLI usage info
+
+	  The usage info for 'pjsip send notify' previously referenced the
+	  chan_sip configuration sip_notify.conf.  Fix this to reference
+	  the correct configuration pjsip_notify.conf.
+
+	  ASTERISK-25590 #close
+
+	  Change-Id: I3898271a8e8a8b1db201741e790ebe2c6bf5cdea
+
+2015-11-23 14:27 +0000 [fc45f4040d]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_sorcery_realtime.c: Fix crash from NULL sorcery object type.
+
+	  If the sorcery object type is not found a NULL is returned.
+	  Unfortunately, sorcery_realtime_filter_objectset() will crash after
+	  complaining about not finding the object type and saying to expect errors.
+
+	  * Use ao2_cleanup() instead of ao2_ref() to prevent the crash.
+
+	  ASTERISK-25165
+	  Reported by Corey Farrell
+
+	  Change-Id: Ic3b64453ea3058cb68d5c26d97d4fe7b8eea2e97
+
+2015-11-20 21:08 +0000 [4875e5ac32]  Matt Jordan <mjordan at digium.com>
+
+	* chan_pjsip: Handle T.38 faxes with direct media bridges
+
+	  When a channel is in a direct media bridge, a re-INVITE may arrive that forces
+	  Asterisk to re-negotiate the media to a T.38 fax. When this occurs, the bridge
+	  must change its technology to a simple bridge, and re-INVITE the media back
+	  to Asterisk.
+
+	  Generally, this logic mostly already exists in Asterisk. However, prior to this
+	  patch, there were a few bugs:
+	  (1) The T.38 framehook currently prevents a channel capable of T.38 faxes from
+	      ever entering into a direct media bridge. This applies even when the only
+	      media being passed over the channel is audio. This patch fixes this bug
+	      by having the framehook specify that it defers caring about any frame type.
+	      This allows the channels to enter into a direct media bridge, which will
+	      be broken when a re-INVITE is received.
+	  (2) When a re-INVITE is received, nothing instructed the bridging layer to
+	      re-inspect the allowed bridging technology. This now occurs when either
+	      a re-INVITE is received from a peer, or when a response is received from
+	      the far end (that is, when the T.38 state changes to either
+	      T38_PEER_REINVITE or T38_LOCAL_REINVITE).
+	  (3) chan_pjsip needs to do a small amount of work to prevent a direct media
+	      bridge from being chosen when a T.38 session is in progress. When a T.38
+	      session supplement has a t38 datastore - which is added when we detect
+	      we should start thinking about T.38 on a channel - we now refuse a native
+	      RTP bridge.
+	  (4) When a BYE request is received, we don't terminate the T.38 session. If
+	      the other side of a T.38 fax survives the hangup (due to the 'g' flag
+	      in Dial, for example), we don't currently re-INVITE the media on the
+	      other channel back to audio. This patch now has res_pjsip_t38 intercept
+	      BYE requests and inform the far side that the T.38 session is terminated.
+	      This naturally causes the correct re-INVITEs to be sent.
+
+	  ASTERISK-25582
+
+	  Change-Id: Iabd6aa578e633d16e6b9f342091264e4324a79eb
+
+2015-11-20 21:07 +0000 [2b94d9a10d]  Matt Jordan <mjordan at digium.com>
+
+	* res/res_pjsip_t38: Add debug statements
+
+	  This patch adds some debug statements to res_pjsip_t38. These statements help
+	  to determine which SDP negotiation callbacks are being executed, and, when
+	  a particular callback exits, why a callback may not have applied its logic
+	  to the local or remote SDP.
+
+	  Change-Id: I61b3fb9183b7ebbb5da8e9f48b59a5d9d7042d77
+
+2015-10-22 09:44 +0000 [af288b2d96]  Matt Jordan <mjordan at digium.com>
+
+	* main/cli: Use proper string methods to check existence of context/exten/app
+
+	  Because the context, extension, and application are stored in stringfields,
+	  checking for them being NULL doesn't work so well. This patch uses the
+	  appropriate string library call, ast_strlen_zero, to see if there is a value
+	  in the context/exten/app values.
+
+	  Change-Id: Ie09623bfdf35f5a8d3b23dd596647fe3c97b9a23
+
+2015-11-18 09:43 +0000 [d27aac0a9d]  Matt Jordan <mjordan at digium.com>
+
+	* res/res_endpoint_stats: Add module to emit endpoint StatsD statistics
+
+	  This patch adds a module that emits StatsD statistics about Asterisk
+	  endpoints. This includes:
+	   * A GUAGE statistic for endpoint states, tracking how many endpoints are in
+	     a particular state.
+	   * A GUAGE statistic for each endpoint, counting the number of channels
+	     currently associated with an endpoint.
+
+	  ASTERISK-25572
+
+	  Change-Id: If7e1333c5aeda8d136850b30c2101c0ee1c97305
+
+2015-11-18 10:07 +0000 [90d9a70789]  Matt Jordan <mjordan at digium.com>
+
+	* res_pjsip/pjsip_options: Add StatsD statistics for PJSIP contacts
+
+	  This patch adds the ability to send StatsD statistics related to the
+	  state of PJSIP contacts. This includes:
+	   * A GUAGE statistic measuring the count of contacts in a particular state.
+	     This measures how many contacts are reachable, unreachable, etc.
+	   * The RTT time for each contact, if those contacts are qualified. This
+	     provides StatsD engines useful time-based data about each contact.
+
+	  ASTERISK-25571
+
+	  Change-Id: Ib8378d73afedfc622be0643b87c542557e0b332c
+
+2015-11-13 10:34 +0000 [75097a0955]  Matt Jordan <mjordan at digium.com>
+
+	* res/res_pjsip_outbound_registration: Add registration statistics for StatsD
+
+	  This patch adds outbound registration statistics for StatsD. This includes
+	  the following:
+	   * A GUAGE metric for the overall count of outbound registrations.
+	   * A GUAGE metric for each state an outbound registration can be in. As the
+	     outbound registrations change state, the overall count of how many
+	     outbound registrations are in the particular state is changed.
+
+	  These statistics are particularly useful for systems with a large number of
+	  SIP trunks, and where measuring the change in state of the trunks is useful
+	  for monitoring.
+
+	  ASTERISK-25571
+
+	  Change-Id: Iba6ff248f5d1c1e01acbb63e9f0da1901692eb37
+
+2015-11-19 09:40 +0000 [8f71263e72]  Matt Jordan <mjordan at digium.com>
+
+	* res/res_pjsip_outbound_registration: Apply configuration on object type load
+
+	  When Asterisk is configured to use a dynamic sorcery backend (such as
+	  res_sorcery_astdb) with 'registration' objects, it will fail to create the
+	  internal state objects associated with the registration objects on module
+	  load. This is due to nothing actually querying for the specific objects
+	  and calling their sorcery apply handler during module load.
+
+	  This patch fixes that by calling get_registrations in the sorcery observer's
+	  object_type_loaded handler. Doing this causes the sorcery backends to be
+	  asked for the current state of all registration objects, which causes the
+	  apply handler to be called and the internal run-time state to be created.
+
+	  ASTERISK-25575 #close
+
+	  Change-Id: Ie9306e797098c6d4da7bcf4a5434a15891508b23
+
+2015-11-11 11:51 +0000 [0b508789ab]  Alexander Traud <pabstraud at compuserve.com>
+
+	* translate: Provide translation modules the result of SDP negotiation.
+
+	  Previously, a trancoding module did not have access to the joint but cached
+	  format. Therefore, the module did not have access to the attributes negotiated
+	  via SDP (line fmtp). Now, a translation module receives the joint format.
+
+	  ASTERISK-25545 #close
+
+	  Change-Id: Id6878a989b50573298dab115d3371ea369e1a718
+
+2015-11-19 01:14 +0000 [1aa552b2a2]  Alexander Traud <pabstraud at compuserve.com>
+
+	* res_format_attr_h264: Do not reset string buffer.
+
+	  When no parameter is present, Asterisk does not generate the line fmtp, as
+	  expected. However, because a buffer was reset, even rtpmap and fmtp of previous
+	  media codecs got removed. Now, Asterisk does not reset other codecs in case of
+	  no parameter for H.264.
+
+	  ASTERISK-25573 #close
+
+	  Change-Id: I93811331f4a28c45418a9e14ee46c0debd47a286
+
+2015-11-18 10:05 +0000 [3354b325c6]  Matt Jordan <mjordan at digium.com>
+
+	* res_statsd: Add functions that support variable arguments
+
+	  Often, the metric names of statistics we are generating for StatsD have some
+	  dynamic component to them. This can be the name of a particular resource, or
+	  some internal status label in Asterisk. With the current set of functions,
+	  callers of the statsd API must first build the metric name themselves, then
+	  pass this to the API functions. This results in a large amount of boilerplate
+	  code and usage of either fixed length static buffers or dynamic memory
+	  allocation, neither of which is desireable.
+
+	  This patch adds two new functions to the StatsD API that support a printf
+	  style format specifier for constructing the metric name. A dynamic string,
+	  allocated in threadstorage, is used to build the metric name. This eases
+	  the burden on users of the StatsD API.
+
+	  Change-Id: If533c72d1afa26d807508ea48b4d8c7b32f414ea
+
+2015-11-17 14:53 +0000 [d4a522d587]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_outbound_registration.c: Be tolerant of short registration timeouts.
+
+	  Change-Id: Ie16f5053ebde0dc6507845393709b4d6a3ea526d
+
+2015-11-17 14:53 +0000 [e44ab3816c]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_outbound_registration.c: Fix 423 response handling.
+
+	  Receiving a 423 Interval Too Brief response after authentication for an
+	  outbound registration attempt results in assuming that the registrar has
+	  rejected the registration permanently.  If there are no configured retries
+	  for fatal responses then the outbound registration is stopped for that
+	  endpoint.
+
+	  For registrations, PJSIP/PJPROJECT intercepts the handling of 423
+	  responses and does not include any authentication in the updated
+	  registration request.  When the updated request is challenged then the
+	  Asterisk code assumes that we were challenged again because the peer
+	  rejected the authentication we sent earlier.
+
+	  * Made registration challenges keep track of the CSeq number to determine
+	  if the received challenge response was for the request we thought we sent.
+	  If the response's CSeq number differs from the CSeq number we last sent
+	  with authentication then authenticate again because it is a challenge to a
+	  different request.
+
+	  Change-Id: I81b4bd36d1be095bab606e34b8b44e6302971b09
+
+2015-11-03 14:36 +0000 [1e0040b88f]  Tyler Cambron <tcambron at digium.com>
+
+	* StatsD: Add res_statsd compatibility
+
+	  Added a new api to res_statsd.c to allow it to receive a
+	  character pointer for the value argument. This allows for a
+	  '+' and a '-' to easily be sent with the value.
+
+	  ASTERISK-25419
+	  Reported By: Ashley Sanders
+
+	  Change-Id: Id6bb53600943d27347d2bcae26c0bd5643567611
+
+2015-11-16 13:56 +0000 [f62b642fe3]  Matt Jordan <mjordan at digium.com>
+
+	* res/res_pjsip: Fix off nominal crash with requests that fail and have a timer
+
+	  When a request is sent using pjsip_endpt_send_request and fails, a condition
+	  exists where the request wrapper, which is an AO2 object, may be de-ref'd
+	  more times than it should. This occurs when the request's callback is called,
+	  and, in the callback, the timer on the PJSIP heap is cancelled. When that
+	  occurs, the request wrapper's lifetime is decremented. When
+	  pjsip_endpt_send_request fails, we unilaterally decrement the lifetime of
+	  the request wrapper again, even though we've already cancelled the reference
+	  associated with the timer.
+
+	  This patch checks the return result of pj_timer_heap_cancel_if_active before
+	  removing the reference associated with the timer. We now only decrement it
+	  in this case if a timer is cancelled as a result of the function call.
+
+	  Change-Id: I21332343a1a019c1117076f9bf2df27be2850102
+
+2015-11-13 14:03 +0000 [fdd2afcd16]  Mark Michelson <mmichelson at digium.com>
+
+	* Confbridge: Add a user timeout option
+
+	  This option adds the ability to specify a timeout, in seconds, for a
+	  participant in a ConfBridge. When the user's timeout has been reached,
+	  the user is ejected from the conference with the CONFBRIDGE_RESULT
+	  channel variable set to "TIMEOUT".
+
+	  The rationale for this change is that there have been times where we
+	  have seen channels get "stuck" in ConfBridge because a network issue
+	  results in a SIP BYE not being received by Asterisk. While these
+	  channels can be hung up manually via CLI/AMI/ARI, adding some sort of
+	  automatic cleanup of the channels is a nice feature to have.
+
+	  ASTERISK-25549 #close
+	  Reported by Mark Michelson
+
+	  Change-Id: I2996b6c5e16a3dda27595f8352abad0bda9c2d98
+
+2015-11-16 04:29 +0000 [7debb986a5]  Alec Davis <sivad.a at paradise.net.nz>
+
+	* app_queue: (try_calling): mutex 'qe->chan' freed more times than we've locked!
+
+	  commit aae45acbd (Mark Michelson 2015-04-15 10:38:02 -0500 6525)
+	  refer ASTERISK-24958
+
+	  above commit removed ast_channel_lock(qe->chan);
+	  but failed to remove corresponding ast_channel_unlock(qe->chan);
+
+	  ASTERISK-25561 #close
+	  Reported Alec Davis
+
+	  Change-Id: Ie05f4e2d08912606178bf1fded57cc022c7a2e1a
+
+2015-11-14 07:02 +0000 [afd9a89e5a]  Joshua Colp <jcolp at digium.com>
+
+	* hashtab: Add NULL check when destroying iterator.
+
+	  The hashtab API is pretty NULL tolerant which has resulted
+	  in remaining callers not doing much checks themselves.
+	  Unfortunately the function to destroy an iterator does not
+	  do a NULL check and will result in a crash if passed NULL.
+	  This change fixes that.
+
+	  ASTERISK-25552 #close
+
+	  Change-Id: Ic1bf8eec3639e5a440f1c941d3ae3893ac6ed619
+
+2015-11-13 14:32 +0000 [c0f2f8de45]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_rfc3326.c: Fix crash when channel goes away.
+
+	  If an authenticated incoming caller does not respond to our 200 OK INVITE
+	  response with an ACK then PJSIP will hangup the call.  Unfortunately,
+	  there is a chance that the session's channel will go away between one use
+	  of the channel pointer and another when building the BYE request because
+	  the BYE is being built by the monitor thread and not the call's serializer
+	  thread.
+
+	  * Added a check to ensure that the thread trying to add the Reason header
+	  is the call's serializer thread.  This ensures that the channel will not
+	  go away on us.
+
+	  Change-Id: I866388d2b97ea2032eaae3f3ab3f1ca6cbd2df89
+
+2015-11-13 14:19 +0000 [4f43b85c92]  Mark Michelson <mmichelson at digium.com>
+
+	* Taskprocessors: Increase high-water mark
+
+	  In practical tests, we have seen certain taskprocessors, specifically
+	  Stasis subscription taskprocessors, cross the recently-added high-water
+	  mark and emit a warning. This high-water mark warning is only intended
+	  to be emitted when things have tanked on the system and things are
+	  heading south quickly. In the practical tests, the Stasis taskprocessors
+	  sometimes had a max depth of 180 tasks in them, and Asterisk wasn't in
+	  any danger at all.
+
+	  As such, this ups the high-water mark to 500 tasks instead. It also
+	  redefines the SIP threadpool request denial number to be a multiple of
+	  the taskprocessor high-water mark.
+
+	  Change-Id: Ic8d3e9497452fecd768ac427bb6f58aa616eebce
+
+2015-11-11 11:46 +0000 [d8d3991390]  Alexander Traud <pabstraud at compuserve.com>
+
+	* format: Register format-attribute module with cached formats.
+
+	  In Asterisk 13, cached formats are created before their corresponding format-
+	  attribute module is registered. Cached formats are involved when a local
+	  extension is called. Therefore, ast_format_generate_sdp_fmtp did not work
+	  on local extensions. This change affects the Opus Codec, H.263 (Plus), H.264,
+	  and format-attribute modules provided externally.
+
+	  ASTERISK-25160 #close
+
+	  Change-Id: I1ea1f0483e5261e2a050112e4ebdfc22057d1354
+
+2015-11-12 11:17 +0000 [367972e42d]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip distributor: Don't send 503 response to responses.
+
+	  When the SIP threadpool is backed up with tasks, we send 503 responses
+	  to ensure that we don't try to overload ourselves. The problem is that
+	  we were not insuring that we were not trying to send a 503 to an
+	  incoming SIP response.
+
+	  This change makes it so that we only send the 503 on incoming requests.
+
+	  Change-Id: Ie2b418d89c0e453cc6c2b5c7d543651c981e1404
+
+2015-11-11 17:11 +0000 [2f9cb7d62b]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip: Deny requests when threadpool queue is backed up.
+
+	  We have observed situations where the SIP threadpool may become
+	  deadlocked. However, because incoming traffic is still arriving, the SIP
+	  threadpool's queue can continue to grow, eventually running the system
+	  out of memory.
+
+	  This change makes it so that incoming traffic gets rejected with a 503
+	  response if the queue is backed up too much.
+
+	  Change-Id: I4e736d48a2ba79fd1f8056c0dcd330e38e6a3816
+
+2015-11-12 06:24 +0000 [4e5bf12b33]  Joshua Colp <jcolp at digium.com>
+
+	* format_cap: Don't append the 'none' format when appending all.
+
+	  When appending all formats of a type all the codecs are iterated
+	  and added. This operation was incorrectly adding the ast_format_none
+	  format which is special in that it is supposed to be used when no
+	  format is present. It shouldn't be appended.
+
+	  ASTERISK-25535
+
+	  Change-Id: I7b00f3bdf4a5f3022e483d6ece602b1e8b12827c
+
+2015-11-11 04:16 +0000 [07583c2888]  Steve Davies <steve at one47.co.uk>
+
+	* Further fixes to improper usage of scheduler
+
+	  When ASTERISK-25449 was closed, a number of scheduler issues mentioned in
+	  the comments were missed. These have since beed raised in ASTERISK-25476
+	  and elsewhere.
+
+	  This patch attempts to collect all of the scheduler issues discovered so
+	  far and address them sensibly.
+
+	  ASTERISK-25476 #close
+
+	  Change-Id: I87a77d581e2e0d91d33b4b2fbff80f64a566d05b
+
+2015-11-11 11:04 +0000 [b818d70533]  Joshua Colp <jcolp at digium.com>
+
+	* threadpool: Handle worker thread transitioning to dead when going active.
+
+	  This change adds handling of dead worker threads when moving them
+	  to be active. When this happens the worker thread is removed from
+	  both the active and idle threads container. If no threads are able
+	  to be moved to active then the pool grows as configured.
+
+	  A unit test has also been added which thrashes the idle timeout
+	  and thread activation to exploit any race conditions between the
+	  two.
+
+	  ASTERISK-25546 #close
+
+	  Change-Id: I6c455f9a40de60d9e86458d447b548fb52ba1143
+
+2015-11-10 09:27 +0000 [4bf84459c7]  Alexander Traud <pabstraud at compuserve.com>
+
+	* rtp_engine: Init a format-attribute module to its RFC defaults.
+
+	  Previously, format-attribute modules relied on an existing fmtp line in SDP
+	  negotiation. However, fmtp is optional for several formats like the Opus Codec.
+	  Now, the format-attribute module is called with an empty fmtp, which allows the
+	  module to initialise itself to RFC defaults. Furthermore now, Asterisk is able
+	  to differentiate between internally and externally created formats.
+
+	  ASTERISK-25537 #close
+
+	  Change-Id: I28f680cef7fdf51c0969ff8da71548edad72ec52
+
+2015-11-09 03:04 +0000 [1bff400df7]  Alexander Traud <pabstraud at compuserve.com>
+
+	* ast_format_cap_get_names: To display all formats, the buffer was increased.
+
+	  ASTERISK-25533 #close
+
+	  Change-Id: Ie1a9d1a6511b3f1a56b93d04475fbf8a4e40010a
+
+2015-11-09 07:04 +0000 [f3ac4d8090]  Alexander Traud <pabstraud at compuserve.com>
+
+	* ast_format_cap: Avoid format creation on module load, use cache instead.
+
+	  Since Asterisk 13, formats are immutable and cached. However while loading a
+	  module like chan_sip, some formats were created instead using cached ones.
+
+	  ASTERISK-25535 #close
+
+	  Change-Id: I479cdc220d5617c840a98f3389b3bd91e91fbd9b
+
+2015-11-06 07:54 +0000 [6d1bdb9d3b]  Walter Doekes <walter+asterisk at wjd.nu>
+
+	* func_callerid: Document that CALLERID(pres) is available.
+
+	  CALLERPRES() says that it's deprecated in favor of CALLERID(num-pres)
+	  and CALLERID(name-pres).  But for channel driver that don't make a
+	  distinction between the two (e.g. SIP), it makes more sense to get/set
+	  both at once.  This change reveals the availability of CALLERID(pres),
+	  CONNECTEDLINE(pres), REDIRECTING(orig-pres), REDIRECTING(to-pres) and
+	  REDIRECTING(from-pres).
+
+	  ASTERISK-25373 #close
+
+	  Change-Id: I5614ae4ab7d3bbe9c791c1adf147e10de8698d7a
+2015-11-06 07:52 +0000 [8410336681]  Walter Doekes <walter+asterisk at wjd.nu>
+
+	* docs: Fix a few typo's in app docs (more then, resourse).
+
+	  Change-Id: Iba57efadf6c0b822e762c7a001bc89611d98afd7
+
+2015-11-06 07:36 +0000 [0d425f2eb4]  Walter Doekes <walter+asterisk at wjd.nu>
+
+	* xmldoc: Improve xmldoc wrapping of 'core show ...' output.
+
+	  Previously, the wrapping did both lookahead and lookback, which,
+	  together with color escape sequences, caused some lines to be wrapped
+	  way earlier than other lines.  This led to inconsistent output.
+
+	  This simplifies the wrapping code and makes it more sane: if maxcolumns
+	  is hit, we simply jump back to the last space and wrap there.
+
+	  ASTERISK-25527 #close
+
+	  Change-Id: I56d01c6f9a812642b1b05535c98d4db48d17c957
+
+2015-11-06 06:57 +0000 [33752e0837]  Sean Bright (license #5060)
+
+	* res_pjsip_sdp_rtp: Enable Opus to be negotiated via SIP/SDP.
+
+	  In SIP/SDP, Opus has two channels always (see RFC 7587 section 7). The actual
+	  amount of channels is negotiated in-band. Therefore now, the Opus codec and its
+	  attribute rtpmap are registered with two channels.
+
+	  ASTERISK-24779 #close
+	  Reported by: PowerPBX
+	  Tested by: Alexander Traud
+	  patches:
+	    asterisk-24779.patch submitted by Sean Bright (license #5060)
+
+	  Change-Id: Ic7ac13cafa1d3450b4fa4987350924b42cbb657b
+
+2015-11-03 16:19 +0000 [6ff48319d9]  Jonathan Rose <jrose at digium.com>
+
+	* taskprocessor: Add high water mark warnings
+
+	  If a taskprocessor's queue grows large, this can indicate that there
+	  may be a problem with tasks not leaving the processor or else that
+	  the number of available task processors for a given type of task is
+	  too low. This patch makes it so that if a taskprocessor's task queue
+	  grows above 100 queued tasks that it will emit a warning message.
+	  Warning messages are emitted only once per task processor.
+
+	  ASTERISK-25518 #close
+	  Reported by: Jonathan Rose
+
+	  Change-Id: Ib1607c35d18c1d6a0575b3f0e3ff5d932fd6600c
+
+2015-11-04 14:31 +0000 [506aea26e6]  Matt Jordan <mjordan at digium.com>
+
+	* main/dial: Protect access to the format_cap structure of the requesting channel
+
+	  When a dial attempt is made that involves a requesting channel, we previously
+	  were not:
+	  a) Protecting access to the native format capabilities structure on the
+	     requesting channel. That is inherently unsafe.
+	  b) Reference bumping the lifetime of the format capabilities structure.
+
+	  In both cases, something else could sneak in, blow away the format
+	  capabilities, and we'd be holding onto an invalid format_cap structure. When
+	  the newly created channel attempts to construct its format capabilities, things
+	  go poorly.
+
+	  This patch:
+	  a) Ensures that we get a reference to the native format capabilities while
+	     the requesting channel is locked
+	  b) Holds a reference to the native format capabilities during the creation
+	     of the new channel.
+
+	  ASTERISK-25522 #close
+
+	  Change-Id: I0bfb7ba8b9711f4158cbeaae96edf9626e88a54f
+
+2015-10-30 22:57 +0000 [d098d00424]  Corey Farrell <git at cfware.com>
+
+	* Fix cli display of build options.
+
+	  A previous commit reduced the AST_BUILDOPTS compiler define to
+	  only include options that affected ABI.  This included some options
+	  that were previously displayed by cli "core show settings".  This
+	  change corrects the CLI display while still restricting buildopts.h
+	  to ABI effecting options only.
+
+	  ASTERISK-25434 #close
+	  Reported by: Rusty Newton
+
+	  Change-Id: Id07af6bedd1d7d325878023e403fbd9d3607e325
+
+2015-11-03 11:15 +0000 [afec1b1b64]  Matt Jordan <mjordan at digium.com>
+
+	* res_pjsip/location: Destroy contact_status objects on contact deletion
+
+	  The contact_status Sorcery objects are currently not destroyed when a contact
+	  is deleted. This causes the contact's last known RTT/status to be 'sticky'
+	  when the contact itself may no longer exist. This patch causes the
+	  contact_status objects associated with both dynamic and static contacts to
+	  be destroyed if the AoR holding those contacts is also destroyed (or via
+	  other paths where a contact may be deleted.)
+
+	  Change-Id: I7feec8b9278cac3c5263a4c0483f4a0f3b62426e
+
+2015-11-03 10:58 +0000 [715f770c9f]  Matt Jordan <mjordan at digium.com>
+
+	* pjsip_configuration: On delete, remove the persistent version of an endpoint
+
+	  When an endpoint is deleted (such as through an API), the persistent endpoint
+	  currently continues to lurk around. While this isn't harmful from a memory
+	  consumption perspective - as all persistent endpoints are reclaimed on
+	  shutdown - it does cause Stasis endpoint related operations to continue
+	  to believe that the endpoint may or may not exist.
+
+	  This patch causes the persistent endpoint related to a PJSIP endpoint to be
+	  destroyed if the PJSIP endpoint is deleted.
+
+	  Change-Id: I85ac707b4d5e6aad882ac275b0c2e2154affa5bb
+2015-11-03 08:15 +0000 [f0f190af08]  Matt Jordan <mjordan at digium.com>
+
+	* main/stasis_endpoints: Fix ContactStatusChange JSON for roundtrip_usec field
+
+	  The JSON packing for the ContactStatusChange event forgot to include the
+	  roundtrip_usec field. As a result, the field never showed up in any event,
+	  even when the data was available. This patch corrects that error by properly
+	  packing the JSON blob with the data.
+
+	  Change-Id: I8df80da659a44010afbd48f645967518ff5daa17
+
+2015-11-02 20:24 +0000 [0393bd6bed]  Corey Farrell <git at cfware.com>
+
+	* chan_sip: Allow websockets to be disabled.
+
+	  This patch adds a new setting "websockets_enabled" to sip.conf.
+	  Setting this to false allows chan_sip to be used without causing
+	  conflicts with res_pjsip_transport_websocket.
+
+	  ASTERISK-24106 #close
+	  Reported by: Andrew Nagy
+
+	  Change-Id: I04fe8c4f2d57b2d7375e0e25826c91a72e93bea7
+
+2015-11-02 17:19 +0000 [6fbffe42e1]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip: Set threadpool max size default to 50.
+
+	  During a stress test of subscriptions, a huge blast of
+	  subscription-related traffic resulted in the threadpool expanding to a
+	  ridiculous number of threads. The balooning of threads resulted in an
+	  increase of memory, which led to a crash due to being out of memory.
+
+	  An easy fix for the particular test was to limit the size of the
+	  threadpool, thus reining in the amount of memory that would be used. It
+	  was decided that there really is no downside to having a non-infinite
+	  default value for the maximum size of the threadpool, so this change
+	  introduces 50 threads as the maximum threadpool size for the SIP
+	  threadpool.
+
+	  ASTERISK-25513 #close
+	  Reported by John Bigelow
+
+	  Change-Id: If0b9514f1d9b172540ce1a6e2f2ffa1f2b6119be
+
+2015-11-02 06:57 +0000 [11e54b1932]  Matt Jordan <mjordan at digium.com>
+
+	* pjsip_options: Schedule/unschedule qualifies on AoR creation/destruction
+
+	  When an AoR is created or destroyed dynamically, the scheduled OPTIONS
+	  requests that qualify the contacts on the AoR are not necessarily started
+	  or destroyed, particularly for persistent contacts created for that AoR.
+	  This patch adds create/update/delete sorcery observers for an AoR, which
+	  schedule/unschedule the qualifies as expected.
+
+	  Change-Id: Ic287ed2e2952a7808ee068776fe966f9554bdf7d
+
+2015-10-30 13:22 +0000 [118d628e08]  Matt Jordan <mjordan at digium.com>
+
+	* Makefile: Add a rule 'basic-pbx' that installs the Basic PBX configs
+
+	  This patch adds a rule for installing the Super Awesome Company based 'Basic
+	  PBX' configuration files. As part of adding this rule, a bit of the content
+	  that makes up installing the configuration files under the 'samples' target
+	  was refactored into a make subroutine for usage by additional later config
+	  make targets.
+
+	  Change-Id: I6c2e27906f73e2919a2b691da0be20ae70302404
+2015-10-29 08:28 +0000 [9a021a42ad]  Joshua Colp <jcolp at digium.com>
+
+	* res_pjsip_pubsub: Fix assertion when UAS dialog creation fails.
+
+	  When compiled with assertions enabled one will occur when destroying
+	  the subscription tree when UAS dialog creation fails. This is because
+	  the code assumes that a dialog will always exist on a subscription
+	  tree when in reality during this specific scenario it won't.
+
+	  This change makes it so a dialog is not removed from the subscription
+	  tree if it is not present.
+
+	  ASTERISK-25505 #close
+
+	  Change-Id: Id5c182b055aacc5e66c80546c64804ce19218dee
+
+2015-10-26 11:42 +0000 [1256aedf66]  Alexander Traud <pabstraud at compuserve.com>
+
+	* chan_sip: Do not send all codecs on INVITE.
+
+	  Since version 13, Asterisk sent all allowed codecs as callee, even when the
+	  caller did not request/support them. In case of dynamic RTP payloads, this led
+	  to the same ID for different codecs, which is not allowed by SIP/SDP. Now, the
+	  intersection between the requested and the supported codecs is send again.
+
+	  ASTERISK-24543 #close
+
+	  Change-Id: Ie90cb8bf893b0895f8d505e77343de3ba152a287
+
+2015-10-24 13:08 +0000 [5f593e7c38]  gtjoseph <george.joseph at fairview5.com>
+
+	* build: GCC 5.1.x catches some new const, array bounds and missing paren issues
+
+	  Fixed 1 issue in each of the affected files.
+
+	  ASTERISK-25494 #close
+	  Reported-by: George Joseph
+	  Tested-by: George Joseph
+
+	  Change-Id: I818f149cd66a93b062df421e1c73c7942f5a4a77
+
+2015-10-20 16:02 +0000 [162acd45f7]  gtjoseph <george.joseph at fairview5.com>
+
+	* res_pjsip:  Add "like" processing to pjsip list and show commands
+
+	  Add the ability to filter output from pjsip list and show commands
+	  using the "like" predicate like chan_sip.
+
+	  For endpoints, aors, auths, registrations, identifyies and transports,
+	  the modification was a simple change of an ast_sorcery_retrieve_by_fields
+	  call to ast_sorcery_retrieve_by_regex.  For channels and contacts a
+	  little more work had to be done because neither of those objects are
+	  true sorcery objects.  That was just removing the non-matching object
+	  from the final container.  Of course, a little extra plumbing in the
+	  common pjsip_cli code was needed to parse the "like" and pass the regex
+	  to the get_container callbacks.
+
+	  Some of the get_container code in res_pjsip_endpoint_identifier was also
+	  refactored for simplicity.
+
+	  ASTERISK-25477 #close
+	  Reported by: Bryant Zimmerman
+	  Tested by: George Joseph
+
+	  Change-Id: I646d9326b778aac26bb3e2bcd7fa1346d24434f1
+
+2015-10-21 11:51 +0000 [c58091737d]  Kevin Harwell <kharwell at digium.com>
+
+	* res_pjsip_outbound_registration: registration stops due to fatal 4xx response
+
+	  During outbound registration it is possible to receive a fatal (any permanent/
+	  non-temporary 4xx, 5xx, 6xx) response from the registrar that is simply due
+	  to a problem with the registrar itself. Upon receiving the failure response
+	  Asterisk terminates outbound registration for the given endpoint.
+
+	  This patch adds an option, 'fatal_retry_interval', that when set continues
+	  outbound registration at the given interval up to 'max_retries' upon receiving
+	  a fatal response.
+
+	  ASTERISK-25485 #close
+
+	  Change-Id: Ibc2c7b47164ac89cc803433c0bbe7063bfa143a2
+
+2015-10-22 17:07 +0000 [ebe69dee0d]  Mark Michelson <mmichelson at digium.com>
+
+	* format_cap: Detect vector allocation failures.
+
+	  A crash was seen on a system that ran out of memory due to Asterisk not
+	  checking for vector allocation failures in format_cap.c. With this
+	  change, if either of the AST_VECTOR_INIT calls fail, we will return a
+	  value indicating failure.
+
+	  Change-Id: Ieb9c59f39dfde6d11797a92b45e0cf8ac5722bc8
+
+2015-10-02 15:32 +0000 [3b19efefef]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip_pubsub: Prevent sending NOTIFY on destroyed dialog.
+
+	  A certain situation can result in our attempting to send a NOTIFY on a
+	  destroyed dialog. Say we attempt to send a NOTIFY to a subscriber, but
+	  that subscriber has dropped off the network. We end up retransmitting
+	  that NOTIFY until the appropriate SIP timer says to destroy the NOTIFY
+	  transaction. When the pjsip evsub code is told that the transaction has
+	  been terminated, it responds in kind by alerting us that the
+	  subscription has been terminated, destroying the subscription, and then
+	  removing its reference to the dialog, thus destroying the dialog.
+
+	  The problem is that when we get told that the subscription is being
+	  terminated, we detect that we have not sent a terminating NOTIFY
+	  request, so we queue up such a NOTIFY to be sent out. By the time that
+	  queued NOTIFY gets sent, the dialog has been destroyed, so attempting to
+	  send that NOTIFY can result in a crash.
+
+	  The fix being introduced here is actually a reintroduction of something
+	  the pubsub code used to employ. We hold a reference to the dialog and
+	  wait to decrement our reference to the dialog until our subscription
+	  tree object is destroyed. This way, we can send messages on the dialog
+	  even if the PJSIP evsub code wants to terminate earlier than we would
+	  like.
+
+	  In doing this, some NULL checks for subscription tree dialogs have been
+	  removed since NULL dialogs are no longer actually possible.
+
+	  Change-Id: I013f43cddd9408bb2a31b77f5db87a7972bfe1e5
+
+2015-09-29 14:53 +0000 [0a346f095f]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip_pubsub: Ensure dialog lock balance.
+
+	  When sending a NOTIFY, we lock the dialog and then unlock the dialog
+	  when finished. A recent change made it so that the subscription tree's
+	  dialog pointer will be set NULL when sending the final NOTIFY request
+	  out. This means that when we attempt to unlock the dialog, we pass a
+	  NULL pointer to pjsip_dlg_dec_lock(). The result is that the dialog
+	  remains locked after we think we have unlocked it. When a response to
+	  the NOTIFY arrives, the monitor thread attempts to lock the dialog, but
+	  it cannot because we never released the dialog lock. This results in
+	  Asterisk being unable to process incoming SIP traffic any longer.
+
+	  The fix in this patch is to use a local pointer to save off the pointer
+	  value of the subscription tree's dialog when locking and unlocking the
+	  dialog. This way, if the subscription tree's dialog pointer is NULLed
+	  out, the local pointer will still have point to the proper place and the
+	  dialog lock will be unlocked as we expect.
+
+	  Change-Id: I7ddb3eaed7276cceb9a65daca701c3d5e728e63a
+
+2015-09-28 16:36 +0000 [ad39508095]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip_pubsub: Prevent crashes on final NOTIFY.
+
+	  The SIP dialog is removed from the subscription tree when the final
+	  NOTIFY is sent. However, after the final NOTIFY is sent, the persistence
+	  update function still attempts to access the cseq from the dialog,
+	  resulting in a crash.
+
+	  This fix removes the subscription persistence at the same time that the
+	  dialog is removed from the subscription tree. This way, there is no
+	  attempt to update persistence when the subscription is being destroyed.
+
+	  Change-Id: Ibb46977a6cef9c51dc95f40f43446e3d11eed5bb
+
+2015-09-17 17:28 +0000 [067f408760]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip_pubsub: Remove serializer when sending final NOTIFY.
+
+	  There have been crashes seen where a taskprocessor's listener is NULL
+	  unexpectedly.
+
+	  Looking at backtraces, the problem was specifically seen in PJSIP
+	  serializers.
+
+	  Subscriptions make the mistake of removing a serializer from a dialog
+	  during subscription tree destruction. Since subscription trees are
+	  reference-counted, guaranteeing the circumstances behind the destruction
+	  are not possible. This makes it so that the dialog serializer can be
+	  removed while not holding the dialog lock. This makes it possible for
+	  the distributor to get a pointer to the dialog serializer and have that
+	  serializer get freed out from under it.
+
+	  The fix for this is to remove the serializer from a subscription dialog
+	  when sending the final NOTIFY. This guarantees that the serializer is
+	  removed with the dialog lock held. By doing this, we guarantee that if
+	  the distributor gains access to the dialog's serializer, it will not be
+	  possible for the serializer to get freed by another thread.
+
+	  Change-Id: I21f5dac33529f65cec45679bdace60670800ff66
+
+2015-09-02 09:14 +0000 [1bcc592765]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip_pubsub: Fix crash on destruction of empty subscription tree.
+
+	  If an old persistent subscription is recreated but then immediately
+	  destroyed because it is out of date, the subscription tree will have no
+	  leaf subscriptions on it. This was resulting in a crash when attempting
+	  to destroy the subscription tree.
+
+	  A simple NULL check fixes this problem.
+
+	  Change-Id: I85570b9e2bcc7260a3fe0ad85904b2a9bf36d2ac
+
+2015-09-01 15:47 +0000 [b3cc2bd7df]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip_pubsub: Solidify lifetime and ownership of objects.
+
+	  There have been crashes and general instability seen in the pubsub code,
+	  so this patch introduces three changes to increase the stability.
+
+	  First, the ownership model for subscriptions has been modified. Due to
+	  RLS, subscriptions are stored in memory as a tree structure. Prior to my
+	  patch, the PJSIP subscription was the owner of the subscription tree.
+	  When the PJSIP subscription told us that it was terminating, we started
+	  destroying the subscription tree along with all of the individual leaf
+	  subscriptions that belong to the tree. The problem with this model is
+	  that the two actors in play here, the PJSIP subscription and the
+	  individual leaf subscriptions, need to have joint ownership of the
+	  subscription tree. So now, the PJSIP subscription and the individual
+	  leaf subscriptions each have a reference to the subscription tree. This
+	  way, we will not actually free memory until no players are left that
+	  care. The PJSIP subscription is a bigger stakeholder, in that if the
+	  PJSIP subscription's reference to the subscription tree is removed, the
+	  subscription tree instructs the leaf subscriptions to shut down and drop
+	  their references to the subscription tree when possible. The individual
+	  leaf subscriptions, upon being told to shut down, can drop their stasis
+	  subscriptions or whatever they use to learn of new state, and then drop
+	  their reference to the subscription tree once they are ready to die.
+
+	  Second, the lifetime of a PJSIP subscription's reference to our
+	  subscription tree has been altered. As I learned from doing a deep dive,
+	  the PJSIP evsub code can tell Asterisk multiple times that the
+	  subscription has been terminated, and not all of these times
+	  are especially helpful. I have altered the message flow that we use for
+	  SIP subscriptions such that we will always drop the PJSIP subscription's
+	  reference to the subscription tree when we send the NOTIFY that
+	  terminates a SIP subscription. This also means that we will now queue
+	  NOTIFY requests to be sent after responding to incoming SUBSCRIBEs so
+	  that we can have predictable state changes from the PJSIP evsub code.
+
+	  Third, the synchronization of operations has been improved. PJSIP can
+	  call into our code from a serializer thread (e.g. upon receiving an
+	  incoming request) or from the monitor thread (e.g. when a subscription
+	  times out). Because of this, there is the possibility of competing
+	  threads stepping on each other. PJSIP attempts to do some
+	  synchronization on its own by always keeping the dialog lock held when
+	  it calls into us. However, since we end up pushing tasks into the
+	  serializer, the result was that serialized operations were not grabbing
+	  the dialog lock and could, as a result, step on something that was being
+	  attempted by a different thread. Now we ensure that serialized
+	  operations grab the dialog lock, then check for extenuating
+	  circumstances, then proceed with their operation if they can.
+
+	  Change-Id: Iff2990c40178dad9cc5f6a5c7f76932ec644b2e5
+
+2015-10-19 15:28 +0000 [c8c65dfa41]  Richard Mudgett <rmudgett at digium.com>
+
+	* strings.c: Fix __ast_str_helper() to always return a terminated string.
+
+	  Users of functions which call __ast_str_helper() such as the ones listed
+	  below are likely to not check the return value for failure so ensuring
+	  that the string is always nil terminated is a good safety measure.
+
+	  ast_str_set_va()
+	  ast_str_append_va()
+	  ast_str_set()
+	  ast_str_append()
+
+	  Change-Id: I36ab2d14bb6015868b49329dda8639d70fbcae07
+
+2015-10-19 15:27 +0000 [b271d4a28a]  Richard Mudgett <rmudgett at digium.com>
+
+	* Add missing failure checks to ast_str_set_va() callers.
+
+	  Change-Id: I0c2cdcd53727bdc6634095c61294807255bd278f
+
+2015-10-21 11:44 +0000 [f2725c8b77]  Joshua Colp <jcolp at digium.com>
+
+	* res_pjsip: Move URI validation to use time.
+
+	  In a realtime based system with a limited number of threadpool threads
+	  it is possible for a deadlock to occur. This happens when permanent
+	  endpoint state is updated, which will cause database queries to be done.
+	  These queries may result in URI validation being done which is done
+	  synchronously using a PJSIP thread. If all PJSIP threads are in use
+	  processing traffic they themselves may be blocked waiting to get the
+	  permanent endpoint container lock when identifying an endpoint.
+
+	  This change moves URI validation to occur at use time instead of
+	  configuration time. While this comes at a cost of not seeing a problem
+	  until you use it it does solve the underlying deadlock problem.
+
+	  ASTERISK-25486 #close
+
+	  Change-Id: I2d7d167af987d23b3e8199e4a68f3359eba4c76a
+
+2015-10-21 08:08 +0000 [84ff075d41]  Alexander Traud <pabstraud at compuserve.com>
+
+	* format: Update the maximum packetization time for iLBC 30.
+
+	  In September 2006, the maximum packetization time (ptime) were set to such a
+	  low value, packetization was disabled for many codecs actually. This was fixed
+	  for many codecs but not for iLBC 30. This enables packetization for iLBC which
+	  can be enabled for example via allow=ilbc:60,gsm,alaw,ulaw in the file sip.conf.
+
+	  ASTERISK-7803
+
+	  Change-Id: I2ef90023d35efb7cb8fe96ed74f53f6846ffad12
+2015-10-21 09:51 +0000 [869ef2a8ee]  Alexander Traud <pabstraud at compuserve.com>
+
+	* chan_sip: Fix autoframing=yes.
+
+	  With Asterisk 13, the structures ast_format and ast_codec changed. Because of
+	  that, the paketization timing (framing) of the RTP channel moved away from the
+	  formats/codecs. In the course of that change, the ptime of the callee was not
+	  honored anymore, when the optional autoframing was enabled.
+
+	  ASTERISK-25484 #close
+
+	  Change-Id: Ic600ccaa125e705922f89c72212c698215d239b4
+
+2015-10-20 22:24 +0000 [9fd2adc204]  Matt Jordan <mjordan at digium.com>
+
+	* rest-api-templates: Wikify error code response reasons
+
+	  Error response code descriptions may contain wiki markup that need to be
+	  escaped. Without this patch, Confluence will reject the document being sent
+	  and the responsible script will raise an exception.
+
+	  Change-Id: I21fcb66fee7f6332381f2b99b1b0195dff215ee5
+
+2015-10-20 12:06 +0000 [72cbb6df55]  Matt Jordan <mjordan at digium.com>
+
+	* funcs/func_holdintercept: Actually add the HOLD_INTERCEPT function
+
+	  When ab803ec342 was committed, it accidentally forgot to actually *add* the
+	  HOLD_INTERCEPT function. This highlights two interesting points:
+	  * Gerrit forces you to put the patch as it is going to into the repo up for
+	    review, which Review Board did not. Yay Gerrit.
+	  * No one apparently bothered to use this feature, or else they don't know about
+	    it. I'm going to go with the latter explanation.
+
+	  ASTERISK-24922
+
+	  Change-Id: Ida38278f259dd07c334a36f9b7d5475b5db72396
+
+2015-10-19 19:59 +0000 [9fc9777fa3]  Matt Jordan <mjordan at digium.com>
+
+	* contrib/scripts/autosupport: Update for Asterisk 13
+
+	  This patch adds some minor tweaks for autosupport to update it for Asterisk 13.
+	  This includes:
+	  * Finally removing most references to Zaptel
+	  * Adding support for some additional 'core' commands, and fixing nomenclature
+	    that generally hasn't been used for some time
+	  * Adding some PJSIP/SIP commands to gather endpoints/peers and active channels
+
+	  Change-Id: Ic997b418cbd9313588b6608e50f47b0ce6f4f1f1
+
+2015-10-14 14:15 +0000 [dc6ec661b3]  mdu113 <mulitskiy at acedsl.com>
+
+	* res_config_pgsql.c: Fix deadlock loading realtime configuration.
+
+	  On v13, loading several thousand PJSIP endpoints on Asterisk start causes
+	  a deadlock most of the time.
+
+	  Thanks to mdu113 for discovering that there was a call to pgsql_exec() not
+	  protected by the pgsql_lock reentrancy lock.
+
+	  {quote}
+	  I believe a code path exists that attempts to use pgsql connection without
+	  locking pgsql_lock.  I believe what happens during that deadlock that I
+	  see is two concurrent threads are both attempting to send query to pgsql,
+	  one of the thread is using a code path without locking pgsql_lock.  If
+	  they managed to send queries at the same time, it seems postgres ignores
+	  one of the queries and replies only to the one of them.  If it happens so
+	  that the thread holding the lock didn't receive the reply it will wait for
+	  it (and hold the lock) forever (or at least for very long time), thus
+	  completely blocking all access to db.
+	  {quote}
+
+	  * Added missing reentrancy locking around pgsql_exec() in find_table().
+
+	  * Moved unlock of pgsql_lock in unload_module() to avoid locking inversion
+	  between the psql_tables list lock and the pgsql_lock.
+
+	  ASTERISK-25455 #close
+	  Reported by:  mdu113
+	  Patches:
+	        res_config_pgsql.c-connlock2.diff (license #5543) patch uploaded by mdu113
+
+	  Change-Id: Id9e7cdf8a3b65ff19964b0cf942ace567938c4e2
+
+2015-10-13 14:13 +0000 [f8707ae9a5]  Olle Johansson (License 5267)
+
+	* channels/chan_sip: Set cause code to 44 on RTP timeout
+
+	  To quote Olle:
+
+	  "When issuing a hangup due to RTP timeouts the cause code is not set. I have
+	  selected 44 based on Cisco's implementation..."
+
+	  ASTERISK-25135 #close
+	  Reported by: Olle Johansson
+	  patches:
+	    rtp-timeout-cause-1.8.diff uploaded by Olle Johansson (License 5267)
+
+	  Change-Id: Ia62100c55077d77901caee0bcae299f8dc7375fc
+
+2015-10-10 15:20 +0000 [486b172b50]  Ivan Poddubny <ivan.poddubny at gmail.com>
+
+	* Build: Add menuselect options for using compiler sanitizers
+
+	  This patch adds menuselect options for building Asterisk with
+	  various sanitizers provided by gcc and clang.
+
+	  When one of *SANITIZER flags is set in menuselect, the appropriate
+	  option is added to CFLAGS ad LDFLAGS for the build.
+
+	  Information on sanitizers in the project wiki:
+	  https://github.com/google/sanitizers/wiki
+
+	  GCC Manual:
+	  https://gcc.gnu.org/onlinedocs/gcc/Debugging-Options.html
+
+	  Clang Compiler User's Manual:
+	  http://clang.llvm.org/docs/UsersManual.html#controlling-code-generation
+
+	  ASTERISK-24718 #close
+	  Reported by: Badalian Vyacheslav
+
+	  Change-Id: Iafa51b792b7bcb20e848b99d16cf362d08590fa0
+
+2015-10-12 11:21 +0000 [e14023ca35]  Richard Mudgett <rmudgett at digium.com>
+
+	* config.c: Fix off-nominal memory leak.
+
+	  Change-Id: I06e346e9a5c63cc5071e7eda537310c4b43bffe0
+
+2015-10-12 11:20 +0000 [a99e821520]  Richard Mudgett <rmudgett at digium.com>
+
+	* config.c: Fix potential memory corruption after [section](+).
+
+	  The memory corruption could happen if the [section](+) is the last section
+	  in the file with trailing comments.  In this case process_text_line() has
+	  left *last_cat is set to newcat and newcat is destroyed.
+
+	  Change-Id: I0d1d999f553986f591becd000e7cc6ddfb978d93
+
+2015-10-12 11:21 +0000 [8d31d2526b]  Richard Mudgett <rmudgett at digium.com>
+
+	* config.c: Fix #include after [section](+).
+
+	  An #include right after a [section](+) would associate any variable
+	  assignments before a new section in the #include with the wrong section.
+
+	  * Fix section association by setting the current section to the appended
+	  section.
+
+	  * Fix '+' and '!' section flag interaction corner case depending upon
+	  which flag came first.  If the '!' came first then it would be ignored.
+	  If the '!' came after then it would affect the appended section.  The '!'
+	  will now no longer be ignored.
+
+	  ASTERISK-25461 #close
+	  Reported by: Sean Pimental
+
+	  Change-Id: Ic9d3191c8758048e2cbce6432f854b32531731c3
+
+2015-10-06 18:01 +0000 [3329c714f7]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip: Fix deadlock when sending out-of-dialog requests.
+
+	  The struct send_request_wrapper has a pjsip lock associated with it that
+	  is created non-recursive.  There is a code path for the struct
+	  send_request_wrapper lock that will attempt to lock it recursively.  The
+	  reporter's deadlock showed that the thread calling endpt_send_request()
+	  deadlocked itself right after the wrapper object got created.
+
+	  Out-of-dialog requests such as MESSAGE, qualify OPTIONS, and unsolicited
+	  MWI NOTIFY messages can hit this deadlock.
+
+	  * Replaced the struct send_request_wrapper pjsip lock with the mutex lock
+	  that can come with an ao2 object since all of Asterisk's mutexes are
+	  recursive.  Benefits include removal of code maintaining the pjsip
+	  non-recursive lock since ao2 objects already know how to maintain their
+	  own lock and the lock will show up in the CLI "core show locks" output.
+
+	  ASTERISK-25435 #close
+	  Reported by: Dmitriy Serov
+
+	  Change-Id: I458e131dd1b9816f9e963f796c54136e9e84322d
+
+2015-10-06 11:05 +0000 [a1435aa3fa]  Stefan Engström <stefanen at kth.se>
+
+	* res/res_rtp_asterisk.c: Fix incorrect assignment of frame->subclass.frame_ending
+
+	  In ast_rtp_read, the value of the variable 'mark' which we try to assign to a
+	  frame->subclass.frame_ending may be 0, 1 or (1<<23), but we should translate
+	  it to 0 or 1.
+
+	  ASTERISK-25451 #close
+	  Change-Id: I53bdf5c026041730184a6a809009c028549ce626
+
+2015-10-07 01:24 +0000 [3357678b94]  Ivan Poddubny <ivan.poddubny at gmail.com>
+
+	* func_presencestate: Return "not_set" when no data is set in AstDB
+
+	  Return AST_PRESENCE_NOT_SET when CustomPresence AstDB key does not
+	  exist, i.e. when a new CustomPresence is added in the dialplan.
+
+	  ASTERISK-25400 #close
+	  Reported by: Andrew Nagy
+
+	  Change-Id: I6fb17b16591b5a55fbffe96f3994ec26b1b1723a
+
+2015-10-06 20:43 +0000 [b714b2152d]  Matt Jordan <mjordan at digium.com>
+
+	* res/res_rtp_asterisk: Fix assignment after ao2 decrement
+
+	  When we decide we will no longer schedule an RTCP write, we remove the
+	  reference to the RTP instance, then assign -1 to the stored scheduler ID
+	  in case something else comes along and wants to see if anything is scheduled.
+
+	  That scheduler ID is on the RTP instance. After 60a9172d7ef2 was merged to
+	  fix the regression introduced by 3cf0f29310, this improper assignment on a
+	  potentially destroyed object started getting tripped on the build agents.
+
+	  Frankly, this should have been crashing a lot more often earlier. I can only
+	  assume that the timing was changed just enough by both changes to start
+	  actually hitting this problem.
+
+	  As it is, simply moving the assignment prior to the ao2 deference is sufficient
+	  to keep the RTP instance from being referenced when it is very, truly,
+	  aboslutely dead.
+
+	  (Note that it is still good practice to assign -1 to the scheduler ID when we
+	  know we won't be scheduling it again, as the ao2 deref *may* not always destroy
+	  the ao2 object.)
+
+	  ASTERISK-25449
+
+	  Change-Id: Ie6d3cb4adc7b1a6c078b1c38c19fc84cf787cda7
+
+2015-10-06 12:40 +0000 [f939e2bd48]  Florian Sauerteig <ffs at ccn.net>
+
+	* chan_sip: Fix port parsing for IPv6 addresses in SIP Via headers.
+
+	  If a Via header containes an IPv6 address and a port number is ommitted,
+	  as it is the standard port, we now leave the port empty and to not set it
+	  to the value after the first colon of the IPv6 address.
+
+	  ASTERISK-25443 #close
+
+	  Change-Id: Ie3c2f05471cd006bf04ed15598589c09577b1e70
+
+2015-10-05 16:53 +0000 [426263a64d]  Richard Mudgett <rmudgett at digium.com>
+
+	* chan_pjsip: Fix crash on reINVITE before initial INVITE completes.
+
+	  Apparently some endpoints attempt to send a reINVITE before completing the
+	  initial INVITE transaction.  In this case PJSIP responds appropriately to
+	  the reINVITE with a 491 INVITE request pending.  Unfortunately chan_pjsip
+	  is using the initial INVITE transaction state to determine if an INVITE is
+	  the initial INVITE or a reINVITE.  Since the initial INVITE transaction
+	  has not been confirmed yet chan_pjsip thinks the reINVITE is an initial
+	  INVITE and starts another PBX thread on the channel.  The extra PBX thread
+	  ensures that hilarity ensues.
+
+	  * Fix checks for a reINVITE on incoming requests to look for the presence
+	  of a to-tag instead of the initial INVITE transaction state.
+
+	  * Made caller_id_incoming_request() determine what to do if there is a
+	  channel on the session or not.  After a channel is created it is too late
+	  to just store the new party id on the session because the session's party
+	  id has already been copied to the channel's caller id.
+
+	  ASTERISK-25404 #close
+	  Reported by: Chet Stevens
+
+	  Change-Id: Ie78201c304a2b13226f3a4ce59908beecc2c68be
+
+2015-10-05 21:34 +0000 [50fa9ff997]  Matt Jordan <mjordan at digium.com>
+
+	* Fix improper usage of scheduler exposed by 5c713fdf18f
+
+	  When 5c713fdf18f was merged, it allowed for scheduled items to have an ID of
+	  '0' returned. While this was valid per the documentation for the API, it was
+	  apparently never returned previously. As a result, several users of the
+	  scheduler API viewed the result as being invalid, causing them to reschedule
+	  already scheduled items or otherwise fail in interesting ways.
+
+	  This patch corrects the users such that they view '0' as valid, and a returned
+	  ID of -1 as being invalid.
+
+	  Note that the failing HEP RTCP tests now pass with this patch. These tests
+	  failed due to a duplicate scheduling of the RTCP transmissions.
+
+	  ASTERISK-25449 #close
+
+	  Change-Id: I019a9aa8b6997584f66876331675981ac9e07e39
+2015-08-26 16:58 +0000 [8f777ab584]  Debian Amtelco <dan at amtelco.com>
+
+	* chan_pjsip: Add Referred-By header to the PJSIP REFER packet.
+
+	  Some systems require the REFER packet to include a Referred-By header.
+	  If the channel variable SIPREFERREDBYHDR is set, it passes that value as the
+	  Referred-By header value.  Otherwise, it adds the current dialog’s local info.
+
+	  Reported by: Dan Cropp
+	  Tested by: Dan Cropp
+
+	  Change-Id: I3d17912ce548667edf53cb549e88a25475eda245
+
+2015-10-03 06:27 +0000 [74635b5638]  Ivan Poddubny <ivan.poddubny at gmail.com>
+
+	* manager: Fix GetConfigJSON returning invalid JSON
+
+	  When GetConfigJSON was introduced back in 1.6, it returned each
+	  section as an array of strings: ["key=value", "key2=value2"].
+	  Afterwards, it was changed a few times and became
+	  ["key": "value", "key2": "value2"], which is not a correct JSON.
+	  This patch fixes that by constructing a JSON object {} instead of
+	  an array [].
+
+	  Also, the keys "istemplate" and "tempates" that are used to
+	  indicate templates and their inherited categories are now wrapped in
+	  quotes.
+
+	  ASTERISK-25391 #close
+	  Reported by: Bojan Nemčić
+
+	  Change-Id: Ibbe93c6a227dff14d4a54b0d152341857bcf6ad8
+
+2015-09-30 17:28 +0000 [40c69e78f5]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_sorcery_memory_cache.c: Fix deadlock with scheduler.
+
+	  A deadlock can happen when a sorcery object is being expired from the
+	  memory cache when at the same time another object is being placed into the
+	  memory cache.  There are a couple other variations on this theme that
+	  could cause the deadlock.  Basically if an object is being expired from
+	  the sorcery memory cache at the same time as another thread tries to
+	  update the next object expiration timer the deadlock can happen.
+
+	  * Add a deadlock avoidance loop in expire_objects_from_cache() to check if
+	  someone is trying to remove the scheduler callback from the scheduler.
+
+	  ASTERISK-25441 #close
+
+	  Change-Id: Iec7b0bdb81a72b39477727b1535b2539ad0cf4dc
+
+2015-10-01 14:30 +0000 [dfeb513e85]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_sorcery_memory_cache.c: Replace inline code with function.
+
+	  Make sorcery_memory_cache_close() call remove_all_from_cache() instead of
+	  partially inlining it.
+
+	  ASTERISK-25441
+
+	  Change-Id: I1aa6cb425b1a4307096f3f914d17af8ec179a74c
+
+2015-10-01 14:27 +0000 [ced0a2d71b]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_sorcery_memory_cache.c: Shutdown in a less crash potential order.
+
+	  Basically you should shutdown in the opposite order of how you setup since
+	  later setup pieces likely depend on earlier setup pieces.  e.g.,
+	  Registering your external API with the rest of the system should be the
+	  last thing setup and the first thing unregistered during shutdown.
+
+	  Change-Id: I5715765b723100c8d3c2642e9e72cc7ad5ad115e
+
+2015-09-30 17:27 +0000 [cc279eea11]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_sorcery_memory_cache.c: Misc tweaks.
+
+	  Change-Id: I8cd32dffbb4f33bb0c39518d6e4c991e73573160
+
+2015-09-30 17:27 +0000 [9af3b613f6]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_sorcery_memory_cache.c: Made use OBJ_SEARCH_MASK.
+
+	  Change-Id: Ibca6574dc3c213b29cc93486e01ccd51f5caa46c
+
+2015-09-30 13:42 +0000 [56ed7b9dd5]  Joshua Colp <jcolp at digium.com>
+
+	* res_rtp_asterisk: Move "Set role" warning to be debug.
+
+	  In practice the set_role API callback can be invoked even
+	  when no ICE is present on an RTP instance. This can occur
+	  if ICE has not been enabled on it.
+
+	  ASTERISK-25438 #close
+
+	  Change-Id: I0e17e4316f0f0d7f095c78c3d4fd73a913b6ba69
+
+2015-09-28 15:31 +0000 [ddebb217f0]  Richard Mudgett <rmudgett at digium.com>
+
+	* sched.c: Add warning about negative time interval request.
+
+	  Change-Id: Ib91435fb45b7f5f7c0fc83d0eec20b88098707bc
+
+2015-09-29 21:15 +0000  Asterisk Development Team <asteriskteam at digium.com>
+
+	* asterisk 13.6.0-rc1 Released.
+
+2015-09-29 16:12 +0000 [bba1c4066b]  Kevin Harwell <kharwell at lunkwill>
+
+	* Release summaries: Add summaries for 13.6.0-rc1
+
+2015-09-29 16:08 +0000 [82c4aecdbb]  Kevin Harwell <kharwell at lunkwill>
+
+	* .version: Update for 13.6.0-rc1
+
+2015-09-29 16:08 +0000 [bc18db7388]  Kevin Harwell <kharwell at lunkwill>
+
+	* .lastclean: Update for 13.6.0-rc1
+
+2015-09-29 16:08 +0000 [b9c53f95e3]  Kevin Harwell <kharwell at lunkwill>
+
+	* realtime: Add database scripts for 13.6.0-rc1
+
+2015-09-29 14:53 +0000 [d30939b6e8]  Kevin Harwell <kharwell at digium.com>
+
+	* ARI: Changed version from 1.8.0 to 1.9.0
+
+	  Change-Id: I510991c60d28d171f47c4b58bba4947f7fc71b13
+
+2015-09-25 18:37 +0000 [5f19c9bade]  Richard Mudgett <rmudgett at digium.com>
+
+	* res/ari/config.c: Fix user sort compare function.
+
+	  Made use the ao2 sort compare template function and OBJ_SEARCH_xxx
+	  identifiers.
+
+	  Change-Id: Ic53005dc5aafa7a36c72300dd89b75fb63c92f4c
+
+2015-09-25 17:26 +0000 [3a85764039]  Richard Mudgett <rmudgett at digium.com>
+
+	* res/ari/config.c: Optimize conf_alloc() object init.
+
+	  * Now conf_alloc() has more off nominal error checking.
+
+	  * Eliminated RAII_VAR() use in conf_alloc().
+
+	  * Eliminated a dubius shortcut when destroying cfg->general in
+	  conf_destructor() that would cause a crash if cfg->general failed to get
+	  allocated.
+
+	  * Add some ACO registration section comments.
+
+	  Change-Id: Ia40c2b1b2d0777d641605118ae019c5a73865e1a
+
+2015-09-25 16:48 +0000 [028033e5a8]  Richard Mudgett <rmudgett at digium.com>
+
+	* res/ari/config.c: Fix conf_alloc() object init.
+
+	  Need to finish initializing the string fields in the ao2 object before
+	  putting any default strings into them.
+
+	  ASTERISK-25383 #close
+	  Reported by:  yaron nahum
+
+	  Change-Id: I9f7f3a03f0c4991a01593abf8697b9a587c0ea84
+
+2015-09-27 20:45 +0000 [90165e306d]  Matt Jordan <mjordan at digium.com>
+
+	* res/res_stasis: Fix accidental subscription to 'all' bridge topic
+
+	  When b99a7052621700a1aa641a1c24308f5873275fc8 was merged, subscribing to a
+	  NULL bridge will now cause app_subscribe_bridge to implicitly subscribe to
+	  all bridges. Unfortunately, the res_stasis control loop did not check that
+	  a bridge changing on a channel's control object was actually also non-NULL.
+	  As a result, app_subscribe_bridge will be called with a NULL bridge when a
+	  channel leaves a bridge. This causes a new subscription to be made to the
+	  bridge. If an application has also subscribed to the bridge, the application
+	  will now have two subscriptions:
+	  (1) The explicit one created by the app
+	  (2) The implicit one accidentally created by the control structure
+
+	  As a result, the 'BridgeDestroyed' event can be sent multiple times. This
+	  patch corrects the control loop such that it only subscribes an application
+	  to a new bridge if the bridge pointer is non-NULL.
+
+	  ASTERISK-24870
+
+	  Change-Id: I3510e55f6bc36517c10597ead857b964463c9f4f
+
+2015-09-04 13:51 +0000 [e1223ff6db]  Scott Griepentrog <scott at griepentrog.com>
+
+	* Scripts: check file versions of Asterisk and dependencies
+
+	  To help in diagnosing mismatched modules and libraries, this
+	  script scans for version, repository, and source information
+	  and reports what is found.
+
+	  ASTERISK-25376 #close
+	  Reported by: Ashley Sanders
+
+	  Change-Id: Ib0642d0fb96712476f59760d6d137a24633fe2d6
+
+2015-09-24 14:56 +0000 [6b1e7583c1]  Richard Mudgett <rmudgett at digium.com>
+
+	* app_queue.c: Force COLP update if outgoing channel name changed.
+
+	  * When a call is answered and the outgoing channel name has changed then
+	  force a connected line update because the channel is no longer the same.
+	  The channel was masqueraded into by another channel.  This is usually
+	  because of a call pickup.
+
+	  Note: Forwarded calls are handled in a controlled manner so the original
+	  channel name is replaced with the forwarded channel.
+
+	  ASTERISK-25423 #close
+	  Reported by: John Hardin
+
+	  Change-Id: Ie275ea9e99c092ad369db23e0feb08c44498c172
+
+2015-09-24 14:20 +0000 [6bf304bf25]  Richard Mudgett <rmudgett at digium.com>
+
+	* app_queue.c: Factor out a connected line update routine.
+
+	  Replace inlined code with update_connected_line_from_peer().
+
+	  ASTERISK-25423
+	  Reported by: John Hardin
+
+	  Change-Id: I33bbd033596fcb0208d41d8970369b4e87b806f3
+
+2015-09-24 13:27 +0000 [e36b5f1e8e]  Richard Mudgett <rmudgett at digium.com>
+
+	* app_dial.c: Make 'A' option pass COLP updates.
+
+	  While the 'A' option is playing the announcement file allow the caller and
+	  peer to exchange COLP update frames.
+
+	  ASTERISK-25423
+	  Reported by: John Hardin
+
+	  Change-Id: Iac6cf89b56d26452c6bb88e9363622bbf23895f9
+
+2015-09-24 12:59 +0000 [747bfac895]  Richard Mudgett <rmudgett at digium.com>
+
+	* app_dial.c: Force COLP update if outgoing channel name changed.
+
+	  * When a call is answered and the outgoing channel name has changed then
+	  force a connected line update because the channel is no longer the same.
+	  The channel was masqueraded into by another channel.  This is usually
+	  because of a call pickup.
+
+	  Note: Forwarded calls are handled in a controlled manner so the original
+	  channel name is replaced with the forwarded channel.
+
+	  ASTERISK-25423
+	  Reported by: John Hardin
+
+	  Change-Id: I2e01f7a698fbbc8c26344a59c2be40c6cd98b00c
+
+2015-09-24 12:37 +0000 [14481d9aa0]  Richard Mudgett <rmudgett at digium.com>
+
+	* app_dial.c: Factor out a connected line update routine.
+
+	  Replace inlined code with update_connected_line_from_peer().
+
+	  ASTERISK-25423
+	  Reported by: John Hardin
+
+	  Change-Id: Ia14f18def417645cd7fb453e1bdac682630a5091
+
+2015-09-23 17:41 +0000 [bbeda190c3]  Richard Mudgett <rmudgett at digium.com>
+
+	* app_dial.c: Remove some no-op code.
+
+	  Change-Id: Ice1884a94315d3cb7e3bbd47a9fba76a27276c54
+
+2015-09-23 14:02 +0000 [f050fa76eb]  Mark Michelson <mmichelson at digium.com>
+
+	* logger: Prevent duplicate dynamic channels from being added.
+
+	  There was a problem observed where the "logger add channel" CLI command
+	  would allow for a channel with the same name to be added multiple times.
+	  This would result in each message being written out to the same file
+	  multiple times.
+
+	  The problem was due to the difference in how logger channel filenames
+	  are stored versus the format they are allowed to be presented when they
+	  are added. For instance, if adding the logger channel "foo" through the
+	  CLI, the result would be a logger channel with the file name
+	  /var/log/asterisk/foo being stored. So when trying to add another "foo"
+	  channel, "foo" would not match "/var/log/asterisk/foo" so we'd happily
+	  add the duplicate channel.
+
+	  The fix presented here is to introduce two new methods in the logger
+	  code:
+	   * make_filename(): given a logger channel name, this creates the
+	     filename for that logger channel.
+	   * find_logchannel(): given a logger channel name, this calls
+	     make_filename() and then traverses the list of logchannels in order
+	     to find a match.
+
+	  This change has made use of make_filename() and find_logchannel()
+	  throughout to more consistently behave.
+
+	  ASTERISK-25305 #close
+	  Reported by Mark Michelson
+
+	  Change-Id: I892d52954d6007d8bc453c3cbdd9235dec9c4a36
+
+2015-09-24 14:49 +0000 [629458d349]  Mark Michelson <mmichelson at digium.com>
+
+	* Do not swallow frames on channels leaving bridges.
+
+	  When leaving a bridge, indications on a channel could be swallowed by
+	  the internal indication logic because it appears that the channel is on
+	  its way to be hung up anyway. One such situation where this is
+	  detrimental is when channels on hold are redirected out of a bridge. The
+	  AST_CONTROL_UNHOLD indication from the bridging code is swallowed,
+	  leaving the channel in question to still appear to be on hold.
+
+	  The fix here is to modify the logic inside ast_indicate_data() to not
+	  drop the indication if the channel is simply leaving a bridge. This way,
+	  channels on hold redirected out of a bridge revert to their expected "in
+	  use" state after the redirection.
+
+	  ASTERISK-25418 #close
+	  Reported by Mark Michelson
+
+	  Change-Id: If6115204dfa0551c050974ee138fabd15f978949
+
+2015-09-22 17:08 +0000 [5f15cd93f0]  Richard Mudgett <rmudgett at digium.com>
+
+	* app_page.c: Fix crash when forwarding with a predial handler.
+
+	  Page uses the async method of dialing with the dial API.  When a call gets
+	  forwarded there is no calling channel available.  If the predial handler
+	  was set then the calling channel could not be put into auto-service
+	  for the forwarded call because it doesn't exist.  A crash is the result.
+
+	  * Moved the callee predial parameter string processing to before the
+	  string is passed to the dial API rather than having the dial API do it.
+	  There are a few benefits do doing this.  The first is the predial
+	  parameter string processing doesn't need to be done for each channel
+	  called by the dial API.  The second is in async mode and the forwarded
+	  channel is to have the predial handler executed on it then the
+	  non-existent calling channel does not need to be present to process the
+	  predial parameter string.
+
+	  * Don't start auto-service on a non-existent calling channel to execute
+	  the predial handler when the dial API is in async mode and forwarding a
+	  call.
+
+	  ASTERISK-25384 #close
+	  Reported by: Chet Stevens
+
+	  Change-Id: If53892b286d29f6cf955e2545b03dcffa2610981
+
+2015-09-03 21:19 +0000 [b50e372394]  Matt Jordan <mjordan at digium.com>
+
+	* ARI: Add events for Contact and Peer Status changes
+
+	  This patch adds support for receiving events regarding Peer status changes
+	  and Contact status changes. This is particularly useful in scenarios where
+	  we are subscribed to all endpoints and channels, where we often want to know
+	  more about the state of channel technology specific items than a single
+	  endpoint's state.
+
+	  ASTERISK-24870
+
+	  Change-Id: I6137459cdc25ce27efc134ad58abf065653da4e9
+
+2015-09-04 12:24 +0000 [3502c0431d]  Matt Jordan <mjordan at digium.com>
+
+	* res/res_stasis_device_state: Allow for subscribing to 'all' device state
+
+	  This patch adds support for subscribing to all device state changes. This is
+	  done either by subscribing to an empty device, e.g., 'eventSource=deviceState:',
+	  or by the WebSocket connection specifying that it wants all state in the
+	  system.
+
+	  ASTERISK-24870
+
+	  Change-Id: I9cfeca1c9e2231bd7ea73e45919111d44d2eda32
+
+2015-09-04 12:25 +0000 [4c9f613309]  Matt Jordan <mjordan at digium.com>
+
+	* ARI: Add the ability to subscribe to all events
+
+	  This patch adds the ability to subscribe to all events. There are two possible
+	  ways to accomplish this:
+	  (1) On initial WebSocket connection. This patch adds a new query parameter,
+	      'subscribeAll'. If present and True, Asterisk will subscribe the
+	      applications to all ARI events.
+	  (2) Via the applications resource. When subscribing in this manner, an ARI
+	      client should merely specify a blank resource name, i.e., 'channels:'
+	      instead of 'channels:12354'. This will subscribe the application to all
+	      resources of the 'channels' type.
+
+	  ASTERISK-24870 #close
+
+	  Change-Id: I4a943b4db24442cf28bc64b24bfd541249790ad6
+
+2015-09-21 08:16 +0000 [ec514ad64d]  Elazar Broad <elazar at thebroadfamily.com>
+
+	* core/logging: Fix logging to more than one syslog channel
+
+	  Currently, Asterisk will log to the last configured syslog
+	  channel in logger.conf. This is due to the fact that the
+	  final call to openlog() supersedes all of the previous calls.
+	  This commit removes the call to openlog() and passes the
+	  facility to ast_log_vsyslog(), along with utilizing the
+	  LOG_MAKEPRI macro to ensure that the message is routed to
+	  the correct facility and with the correct priority.
+
+	  ASTERISK-25407 #close
+	  Reported by: Elazar Broad
+	  Tested by: Elazar Broad
+
+	  Change-Id: Ie2a2416bc00cce1b04e99ef40917c2011953ddd2
+
+2015-09-21 18:06 +0000 [aeddee39fb]  Kevin Harwell <kharwell at digium.com>
+
+	* app_record: RECORDED_FILE variable not being populated
+
+	  The RECORDED_FILE variable is empty unless a '%d' is specified in the filename.
+	  This patch makes it so the variable is always set to the filename.
+
+	  ASTERISK-25410 #close
+
+	  Change-Id: I4ec826d8eb582ae2ad184e717be8668b74d37653
+
+2015-09-16 08:22 +0000 [2bd27d1222]  Joshua Colp <jcolp at digium.com>
+
+	* pbx: Update device and presence state when changing a hint extension.
+
+	  When changing a hint extension without removing the hint first the
+	  device state and presence state is not updated. This causes the state
+	  of the hint to be that of the previous extension and not the current
+	  one. This state is kept until a state change occurs as a result of
+	  something (presence state change, device state change).
+
+	  This change updates the hint with the current device and presence
+	  state of the new extension when it is changed. Any state callbacks
+	  which may have been added before the hint extension is changed are
+	  also informed of the new device and presence state if either have
+	  changed.
+
+	  ASTERISK-25394 #close
+
+	  Change-Id: If268f1110290e502c73dd289c9e7e7b27bc8432f
+
+2015-09-17 16:34 +0000 [c94f46080f]  Scott Griepentrog <scott at griepentrog.com>
+
+	* CHAOS: avoid crash if string create fails
+
+	  Validate string buffer allocation before using them.
+
+	  ASTERISK-25323
+
+	  Change-Id: Ib9c338bdc1e53fb8b81366f0b39482b83ef56ce0
+
+2015-09-17 04:52 +0000 [b59c4d82b5]  Walter Doekes <walter+asterisk at wjd.nu>
+
+	* chan_sip: Fix From header truncation for extremely long CALLERID(name).
+
+	  The CALLERID(num) and CALLERID(name) and other info are placed into the
+	  `char from[256]` in initreqprep. If the name was too long, the addr-spec
+	  and params wouldn't fit.
+
+	  Code is moved around so the addr-spec with params is placed there first,
+	  and then fitting in as much of the display-name as possible.
+
+	  ASTERISK-25396 #close
+
+	  Change-Id: I33632baf024f01b6a00f8c7f35c91e5f68c40260
+
+2015-09-17 16:59 +0000 [4cc59533b9]  Richard Mudgett <rmudgett at digium.com>
+
+	* CHAOS: res_pjsip_diversion avoid crash if allocation fails
+
+	  Validate ast_malloc buffer returned before using it in
+	  set_redirecting_value().
+
+	  ASTERISK-25323
+
+	  Change-Id: I15d2ed7cb0546818264c0bf251aa40adeae83253
+
+2015-09-17 16:47 +0000 [4fb95bbc4e]  Kevin Harwell <kharwell at digium.com>
+
+	* app_queue: AgentComplete event has wrong reason
+
+	  When a queued caller transfers an agent to another extension sometimes the
+	  raised AgentComplete event has a reason of "caller" and sometimes "transfer".
+	  Since a transfer has taken place this should always be transfer. This occurs
+	  because sometimes the stasis hangup event arrives before the transfer event
+	  thus writing a different reason out.
+
+	  With this patch, when a hangup event is received during a transfer it will
+	  check to see if the channel that is hanging up is part of a transfer. If so
+	  it will return and let the subsequently received transfer event handler take
+	  care of the cleanup.
+
+	  ASTERISK-25399 #close
+
+	  Change-Id: Ic63c49bd9a5ed463ea7a032fd2ea3d63bc81a50d
+
+2015-09-17 13:09 +0000 [fb6b5c684b]  Scott Griepentrog <scott at griepentrog.com>
+
+	* PJSIP: avoid crash when getting rtp peer
+
+	  Although unlikely, if the tech private is returned as
+	  a NULL, chan_pjsip_get_rtp_peer() would crash.
+
+	  ASTERISK-25323
+
+	  Change-Id: Ie231369bfa7da926fb2b9fdaac228261a3152e6a
+
+2015-09-17 11:31 +0000 [6409e7b11a]  Kevin Harwell <kharwell at digium.com>
+
+	* app_queue: Crash when transferring
+
+	  During some transfer scenarios involving queues Asterisk would sometimes
+	  crash when trying to obtain a channel snapshot (could happen on caller or
+	  member channels). This occurred because the underlying channel had already
+	  disappeared when trying to obtain the latest snapshot.
+
+	  This patch adds a reference to both the member and caller channels that
+	  extends to the lifetime of the queue'd call, thus making sure the channels
+	  will always exist when retrieving the latest snapshots.
+
+	  ASTERISK-25185 #close
+	  Reported by: Etienne Lessard
+
+	  Change-Id: Ic397fa68fb4ff35fbc378e745da9246a7b552128
+
+2015-09-16 17:36 +0000 [fe5077b1f8]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip_pubsub: Eliminate race during initial NOTIFY.
+
+	  There is a slim chance of a race condition occurring where two threads
+	  can both attempt to manipulate the same area.
+
+	  Thread A can be handling an incoming initial SUBSCRIBE request. Thread A
+	  lets the specific subscription handler know that the subscription has
+	  been established.
+
+	  At this point, Thread B may detect a state change on the subscribed
+	  resource and queue up a notification task on Thread C, the subscription
+	  serializer thread.
+
+	  Now Thread A attempts to generate the initial NOTIFY request to send to
+	  the subscriber at the same time that Thread C attempts to generate a
+	  state change NOTIFY request to send to the subscriber.
+
+	  The result is that Threads A and C can step on the same memory area,
+	  resulting in a crash. The crash has been observed as happening when
+	  attempting to allocate more space to hold the body for the NOTIFY.
+
+	  The solution presented here is to queue the subscription establishment
+	  and initial NOTIFY generation onto the subscription serializer thread
+	  (Thread C in the above scenario). This way, there is no way that a state
+	  change notification can occur before the initial NOTIFY is sent, and if
+	  there is a quick succession of NOTIFYs, we can guarantee that the two
+	  NOTIFY requests will be sent in succession.
+
+	  Change-Id: I5a89a77b5f2717928c54d6efb9955e5f6f5cf815
+
+2015-08-28 15:42 +0000 [b88c54fa4b]  Alexander Traud <pabstraud at compuserve.com>
+
+	* translate: Fix transcoding while different in frame size.
+
+	  When Asterisk translates between codecs, each with a different frame size (for
+	  example between iLBC 30 and Speex-WB), too large frames were created by
+	  ast_trans_frameout. Now, ast_trans_frameout is called with the correct frame
+	  length, creating several frames when necessary. Affects all transcoding modules
+	  which used ast_trans_frameout: GSM, iLBC, LPC10, and Speex.
+
+	  ASTERISK-25353 #close
+
+	  Change-Id: I2e229569d73191d66a4e43fef35432db24000212
+
+2015-09-10 17:19 +0000 [5c713fdf18]  Mark Michelson <mmichelson at digium.com>
+
+	* scheduler: Use queue for allocating sched IDs.
+
+	  It has been observed that on long-running busy systems, a scheduler
+	  context can eventually hit INT_MAX for its assigned IDs and end up
+	  overflowing into a very low negative number. When this occurs, this can
+	  result in odd behaviors, because a negative return is interpreted by
+	  callers as being a failure. However, the item actually was successfully
+	  scheduled. The result may be that a freed item remains in the scheduler,
+	  resulting in a crash at some point in the future.
+
+	  The scheduler can overflow because every time that an item is added to
+	  the scheduler, a counter is bumped and that counter's current value is
+	  assigned as the new item's ID.
+
+	  This patch introduces a new method for assigning scheduler IDs. Instead
+	  of assigning from a counter, a queue of available IDs is maintained.
+	  When assigning a new ID, an ID is pulled from the queue. When a
+	  scheduler item is released, its ID is pushed back onto the queue. This
+	  way, IDs may be reused when they become available, and the growth of ID
+	  numbers is directly related to concurrent activity within a scheduler
+	  context rather than the uptime of the system.
+
+	  Change-Id: I532708eef8f669d823457d7fefdad9a6078b99b2
+
+2015-08-21 21:50 +0000 [865377fc38]  Rodrigo Ramírez Norambuena <a at rodrigoramirez.com>
+
+	* chan_sip.c: Validation on module reload
+
+	  Change validation on reload module because now used the cli function for
+	  reload. The sip_reload() function never fail and ever return NULL for this
+	  reason on reload() now use the call the sip_reload() and return
+	  AST_MODULE_LOAD_SUCCESS.
+
+	  This problem is dectected on reload by PUT method on ARI, getting always
+	  404 http code when the module is reloaded.
+
+	  ASTERISK-25325 #close
+	  Reporte by: Rodrigo Ramírez Norambuena
+
+	  Change-Id: I41215877fb2cfc589e0d4d464000cf6825f4d7fb
+
+2015-08-21 17:39 +0000 [e75aff53e6]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_pubsub.c: Mark ast_sip_create_subscription() as not used.
+
+	  Change-Id: I2b8db18eac36c01a5c7eb9467699124e203fd093
+
+2015-09-09 12:24 +0000 [4d91d01df1]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_pubsub.c: Add some notification comments.
+
+	  Change-Id: Ie62ff1f4b7adc1a12fa0303f53926af249b25e20
+
+2015-08-21 18:01 +0000 [f36a9d1221]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_pubsub.c: Set dlg_status code instead of sending SIP response.
+
+	  We should not try to send a SIP response message because we may be
+	  restoring a persistent subscription where we are not responding to a SIP
+	  request.
+
+	  Change-Id: Id89167ef90320c5563f37e632db0dda6cb9e7dec
+
+2015-08-21 17:40 +0000 [94582f8fab]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_pubsub.c: Fix off-nominal memory leak.
+
+	  Fix off-nominal visited vector leak in build_resource_tree().
+
+	  Change-Id: If0399c7941c9c0b1038bcfb7b9a371760977831c
+
+2015-08-21 15:26 +0000 [8b3ed52239]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_pubsub.c: Fix one byte buffer overrun error.
+
+	  ast_sip_pubsub_register_body_generator() did not account for the null
+	  terminator set by sprintf() in the allocated output buffer.
+
+	  Change-Id: I388688a132e479bca6ad1c19275eae0070969ae2
+
+2015-08-21 15:25 +0000 [4329bd1e4c]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_pubsub.c: Use ast_alloca() instead of alloca().
+
+	  Change-Id: Ia396096b4fedc2874649ca11137612c3f55e83e3
+
+2015-08-21 11:04 +0000 [a456a20ecf]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_pubsub.c: Add missing error return in load_module().
+
+	  Change-Id: I15debd0f717f16ee2f78e7f56151c3b3b97b72fc
+
+2015-08-21 11:03 +0000 [f58f4c6e27]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip/location.c: Use the builtin ao2_callback() match function instead.
+
+	  Change-Id: I364906d6d2bad3472929986704a0286b9a2cbe3f
+
+2015-09-10 09:49 +0000 [9d1f176e29]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip: Copy default_from_user to avoid crash.
+
+	  The default_from_user retrieval function was pulling the
+	  default_from_user from the global configuration struct in an unsafe way.
+	  If using a database as a backend configuration store, the global
+	  configuration struct is short-lived, so grabbing a pointer from it
+	  results in referencing freed memory.
+
+	  The fix here is to copy the default_from_user value out of the global
+	  configuration struct.
+
+	  Thanks go to John Hardin for discovering this problem and proposing the
+	  patch on which this fix is based.
+
+	  ASTERISK-25390 #close
+	  Reported by Mark Michelson
+
+	  Change-Id: I6b96067a495c1259da768f4012d44e03e7c6148c
+
+2015-09-10 08:39 +0000 [1dd0e220bf]  Matt Jordan <mjordan at digium.com>
+
+	* res/res_pjsip_nat: Ignore REGISTER requests when looking for a Record-Route
+
+	  We will only rewrite the Contact header if there is no Record-Route header in
+	  the received request. If a malfunctioning proxy places a Record-Route header
+	  into a REGISTER request, we will decide that we shouldn't update the IP/port
+	  in the Contact header, and we will end up storing a contact with an AoR that
+	  contains the NAT'd IP address.
+
+	  While it is nice to have the proxy *not* send a Record-Route in a REGISTER
+	  request, it's also a good idea to not process the header in a non-dialog
+	  message. This patch updates the code to explicitly ignore the Record-Route
+	  header in REGISTER requests.
+
+	  ASTERISK-25387 #close
+
+	  Change-Id: I4bd3bcccc4003d460cc354d986b0dea2e433ef3f
+
+2015-09-03 21:15 +0000 [4eedd9ef9d]  Matt Jordan <mjordan at digium.com>
+
+	* main/config_options: Check for existance of internal object before derefing
+
+	  Asterisk can load and register an object type while still having an invalid
+	  sorcery mapping. This can cause an issue when a creation call is invoked.
+	  For example, mis-configuring PJSIP's endpoint identifier by IP address mapping
+	  in sorcery.conf will cause the sorcery mechanism to be invalidated; however, a
+	  subsequent ARI invocation to create the object will cause a crash, as the
+	  internal type may not be registered as sorcery expects.
+
+	  Merely checking for a NULL pointer here solves the issue.
+
+	  Change-Id: I54079fb94a1440992f4735a9a1bbf1abb1c601ac
+2015-09-09 16:46 +0000 [71408df2b8]  Alexander Anikin <may213 at yandex.ru>
+
+	* chan_ooh323: Add ProgressIndicator IE with inband info available
+
+	  Add ProgressIndicator IE with inband info present to Progress and
+	  Alerting Q.931 message
+
+	  ASTERISK-25227 #close
+	  Reported by: Alexandr Dranchuk
+
+	  Change-Id: I326ad13cb1db9a72b3fd902bafed3c28a3684203
+2015-09-08 10:35 +0000 [f72f9ceefc]  Scott Griepentrog <scott at griepentrog.com>
+
+	* pjsip: avoid possible crash req_caps allocation failure
+
+	  Make certain that the pjsip session has not failed to
+	  allocate the format capabilities structure, which can
+	  otherwise cause a crash when referenced.
+
+	  ASTERISK-25323
+
+	  Change-Id: I602790ba12714741165e441cc64a3ecde4cb5750
+
+2015-09-03 14:07 +0000 [fbf720db91]  Jonathan Rose <jrose at digium.com>
+
+	* ParkAndAnnounce: Add variable inheritance
+
+	  In Asterisk 11, the announcer channel would receive channel variables
+	  from the channel being parked by means of normal channel inheritance.
+	  This functionality was lost during the big res_parking project in
+	  Asterisk 12. This patch restores that functionality.
+
+	  ASTERISK-25369 #close
+	  Review: https://gerrit.asterisk.org/#/c/1180/
+
+	  Change-Id: Ie47e618330114ad2ea91e2edcef1cb6f341eed6e
+
+2015-09-04 16:33 +0000 [695f26cbb7]  David M. Lee <dlee at respoke.io>
+
+	* res_rtp_asterisk: Add more ICE debugging
+
+	  In working through a recent ICE negotiation bug, I found the debug
+	  logging in res_rtp_asterisk to be lacking. This patch adds a number of
+	  debug and warning statements that were helpful.
+
+	  Change-Id: I950c6d8f13a41f14b3d6334b4cafe7d4e997be80
+2015-09-01 10:16 +0000 [4ed9c9a280]  Guido Falsi <madpilot at freebsd.org>
+
+	* Core/General: Add #ifdef needed on FreeBSD.
+
+	  pthread_attr_init() defaults to PTHREAD_EXPLICIT_SCHED on FreeBSD
+	  too.
+
+	  ASTERISK-25310 #close
+	  Reported by: Guido Falsi
+
+	  Change-Id: Iae6befac9028b5b9795f86986a4a08a1ae6ab7c4
+
+2015-09-08 07:21 +0000 [5469caa9dd]  Joshua Colp <jcolp at digium.com>
+
+	* res_pjsip: Use hash for contact object identity instead of Contact URI.
+
+	  In the wild it is possible for Contact URIs to be quite long as
+	  parameters can exist on them. This can present a problem when storing
+	  them in the AstDB as the URI is used as part of the object name and
+	  there is a fixed length limit for the AstDB. This will cause
+	  the contact to not get stored.
+
+	  This change uses the MD5 hash of the Contact URI as part of the
+	  object name instead. This has a fixed length which is guaranteed
+	  to not exceed the AstDB length limit.
+
+	  ASTERISK-25295 #close
+
+	  Change-Id: Ie8252a75331ca00b41b9f308f42cc1fbdf701a02
+
+2015-09-07 13:19 +0000 [480c443e26]  Alexander Anikin <may213 at yandex.ru>
+
+	* chan_ooh323: call ast_rtp_instance_stop on ooh323_destroy
+
+	      Call ast_rtp_instance_stop on ooh323_destroy to free resources
+	      allocated by rtp instance
+
+	      ASTERISK-25299 #close
+	      Report by: Alexandr Dranchuk
+
+	  Change-Id: I455096bd7da016b871afe90af86067c2c7c9f33f
+
+2015-09-07 11:15 +0000 [c3e6debdb9]  Matt Jordan <mjordan at digium.com>
+
+	* res/res_pjsip: Purge contacts when an AoR is deleted
+
+	  When an AoR is deleted by an external mechanism, such as through ARI, we
+	  currently do not remove dynamic contacts that were created for that AoR as a
+	  result of a received REGISTER request. As a result, re-creating the AoR will
+	  cause the dynamic contact to be interpreted as a persistent contact, leading
+	  to some rather strange state being created for the contacts/endpoints.
+
+	  This patch adds a sorcery observer for the 'aor' object. When a delete is
+	  issued on the underlying sorcery object, the observer is called, and all
+	  contacts created and persisted in sorcery for that AoR are also removed. Note
+	  that we don't want to perform this action when an AO2 object that is an AoR is
+	  destroyed, as the AoR can still exist in the backing storage (and we would
+	  thus be removing valid contacts from an AoR that still "exists".)
+
+	  ASTERISK-25381 #close
+
+	  Change-Id: I6697e51ef6b2858b5d63401f35dc378bb0f90328
+
+2015-09-05 14:58 +0000 [78d0b9d97e]  Matt Jordan <mjordan at digium.com>
+
+	* channels/pjsip/dialplan_functions: Add an option for extracting the SIP call-id
+
+	  This patch adds a new option to the CHANNEL function that allows for the
+	  extraction of the SIP call-id. It is used in conjunction with the 'pjsip'
+	  option, and will return the Call-ID of the INVITE request that established
+	  the PJSIP channel.
+
+	  ASTERISK-25352
+
+	  Change-Id: I278d1f8bcfe3a53c5aa1dadebc14e92b0abd476a
+
+2015-09-04 16:06 +0000 [61c6c6aa6c]  David M. Lee <dlee at respoke.io>
+
+	* Fix when remote candidates exceed PJ_ICE_MAX_CAND
+
+	  We were passing the wrong count into pj_ice_sess_create_check_list(),
+	  causing the create to fail if we ever received more than PJ_ICE_MAX_CAND
+	  candidates.
+
+	  Change-Id: I0303d8e1ecb20a8de9fe629a3209d216c4028378
+
+2015-09-04 14:40 +0000 [ac62928d6b]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip: Change default from user value.
+
+	  When Asterisk sends an outbound SIP request, if there is no direct
+	  reason to place a specific value for the username in the From header,
+	  Asterisk would generate a UUID. For example, this would happen when
+	  sending outbound OPTIONS requests when qualifying or when sending
+	  outbound INVITE requests when originating (if no explicit caller ID were
+	  provided). The issue is that some SIP providers reject these sorts of
+	  requests with a "Name too long" error response.
+
+	  This patch aims to fix this by changing the default outbound username in
+	  From headers to "asterisk". This value can be overridden by changing the
+	  default_from_user option in the global options if desired.
+
+	  ASTERISK-25377 #close
+	  Reported by Mark Michelson
+
+	  Change-Id: I6a4d34a56ff73ff4f661b0075aeba5461b7f3190
+
+2015-09-04 09:26 +0000 [6002472a62]  Scott Griepentrog <scott at griepentrog.com>
+
+	* endpoint snapshot: avoid second cleanup on alloc failure
+
+	  In ast_endpoint_snapshot_create(), a failure to init the
+	  string fields results in two attempts to ao2_cleanup the
+	  same pointer.  Removed RAII_VAR to eliminate problem.
+
+	  ASTERISK-25375 #close
+	  Reported by: Scott Griepentrog
+
+	  Change-Id: If4d9dfb1bbe3836b623642ec690b6d49b25e8979
+
+2015-09-04 05:33 +0000 [d32e516c7c]  Martin Tomec <tomec.martin at gmail.com>
+
+	* res/pjsip: Mark WSS transport as secure
+
+	  Pjsip is refusing to use unsecure transport with "sips" in url.
+	  WSS should be considered as secure transport.
+
+	  ASTERISK-24602 #comment Partially fixed by setting WSS as secure
+
+	  Change-Id: Iddac406c6deba6240c41a603b8859dfefe1a5353
+
+2015-09-02 17:26 +0000 [ad9cb6c2ce]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip: Fix contact refleak on stateful responses.
+
+	  When sending a stateful response, creation of the transaction can fail,
+	  most commonly because we are trying to create a transaction from a
+	  retransmitted request. When creation of the transaction fails, we end up
+	  leaking a reference to a contact that was bumped when the response was
+	  created.
+
+	  This patch adds the missing deref and fixes the reference leak.
+
+	  Change-Id: I2f97ad512aeb1b17e87ca29ae0abacb4d6395f07
+
+2015-09-02 12:41 +0000 [cc1363209e]  Joshua Colp <jcolp at digium.com>
+
+	* pbx: Fix crash when issuing "core show hints" with long pattern match.
+
+	  When issuing the "core show hints" CLI command a combination of both
+	  the hint extension and context is created. This uses a fixed size
+	  buffer expecting that the extension will not exceed maximum extension
+	  length. When the extension is actually a pattern match this constraint
+	  does not hold true, and the extension may exceed the maximum extension
+	  length. In this case extra characters are written past the end of the
+	  fixed size buffer.
+
+	  This change makes it so the construction of the combined hint extension
+	  and context can not exceed the size of the buffer.
+
+	  ASTERISK-25367 #close
+
+	  Change-Id: Idfa1b95d0d4dc38e675be7c1de8900b3f981f499
+
+2015-09-01 09:05 +0000 [d58c8d73af]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip_pubsub: re-re-fix persistent subscription storage.
+
+	  A recent change to res_pjsip_pubsub switched to using pjsip_msg_print as
+	  a means of writing an appropriate packet to persistent storage. While
+	  this partially solved the issue, it had its own problems.
+	  pjsip_msg_print will always add a Content-Length header to the message
+	  it prints. Frequent restarts of Asterisk can result in persistent
+	  subscriptions being written with five or more Content-Length headers. In
+	  addition, sometimes some apparent corruption of individual headers could
+	  be seen.
+
+	  This aims to fix the problem by not running a parsed message through an
+	  interpreter but rather by taking the raw message and saving it. The
+	  logic for what to save is going to be different depending on whether a
+	  SUBSCRIBE was received from the wire or if it was pulled from
+	  persistence. When receiving a packet from the wire, when using a
+	  streaming transport, the rdata->pkt_info.packet may contain multiple SIP
+	  messages or fragments. However, the rdata->msg_info.msg_buf will always
+	  contain the current SIP message to be processed. When pulling from
+	  persistence, though, the rdata->msg_info.msg_buf will be NULL since no
+	  transport actually handled the packet. However, since we know that we
+	  will always ever pull one SIP message from persistence, we are free to
+	  save directly from rdata->pkt_info.packet instead.
+
+	  ASTERISK-25365 #close
+	  Reported by Mark Michelson
+
+	  Change-Id: I33153b10d0b4dc8e3801aaaee2f48173b867855b
+
+2015-08-31 15:24 +0000 [03fe79f29e]  Mark Michelson <mmichelson at digium.com>
+
+	* Fix deadlock on presence state changes.
+
+	  A deadlock was observed where three threads were competing for different
+	  locks:
+
+	  * One thread held the hints lock and was attempting to lock a specific
+	    hint.
+	  * One thread was holding the specific hint's lock and was attempting to
+	    lock the contexts lock
+	  * One thread was holding the contexts lock and attempting to lock the
+	    hints lock.
+
+	  Clearly the second thread was doing the wrong thing here. The fix for
+	  this is to make sure that the hint's lock is not held on presence state
+	  changes. Something similar is already done (and commented about) for
+	  device state changes.
+
+	  ASTERISK-25362 #close
+	  Reported by Mark Michelson
+
+	  Change-Id: I15ec2416b92978a4c0c08273b2d46cb21aff97e2
+
+2015-08-29 10:36 +0000 [a676ba2aad]  Joshua Colp <jcolp at digium.com>
+
+	* taskprocessor: Fix race condition between unreferencing and finding.
+
+	  When unreferencing a taskprocessor its reference count is checked
+	  to determine if it should be unlinked from the taskprocessors
+	  container and its listener shut down. In between the time when the
+	  reference count is checked and unlinking it is possible for
+	  another thread to jump in, find it, and get a reference to it. If
+	  the thread then uses the taskprocessor it may find that it is not
+	  in the state it expects.
+
+	  This change locks the taskprocessors container during almost the
+	  entire unreference operation to ensure that any other thread which
+	  may attempt to find the taskprocessor has to wait.
+
+	  ASTERISK-25295
+
+	  Change-Id: Icb842db82fe1cf238da55df92e95938a4419377c
+
+2015-08-28 20:22 +0000 [1b1561f4c8]  Joshua Colp <jcolp at digium.com>
+
+	* res_pjsip_sdp_rtp: Fix multiple keepalive scheduled items.
+
+	  The keepalive support in res_pjsip_sdp_rtp currently assumes
+	  that a stream will only be negotiated once. This is false.
+	  If the stream is replaced and later added back it can be
+	  negotiated again causing multiple keepalive scheduled items
+	  to exist. This change explicitly deletes the existing
+	  keepalive scheduled item before adding the new one.
+
+	  The res_pjsip_sdp_rtp module also does not stop RTP
+	  keepalives or timeout timer if the stream has been
+	  replaced. This change adds a callback to the session media
+	  interface to allow a media stream to be stopped without
+	  the resources being destroyed. This allows the scheduled
+	  items and RTP to be stopped when the stream no longer
+	  exists.
+
+	  ASTERISK-25356 #close
+
+	  Change-Id: Ibe6a7cc0927c87326fd5f1c0d4ad889dbfbea1de
+
+2015-08-28 19:57 +0000 [85e1cb51b2]  Joshua Colp <jcolp at digium.com>
+
+	* sched: ast_sched_del may return prematurely due to spurious wakeup
+
+	  When deleting a scheduled item if the item in question is currently
+	  executing the ast_sched_del function waits until it has completed.
+	  This is accomplished using ast_cond_wait. Unfortunately the
+	  ast_cond_wait function can suffer from spurious wakeups so the
+	  predicate needs to be checked after it returns to make sure it has
+	  really woken up as a result of being signaled.
+
+	  This change adds a loop around the ast_cond_wait to make sure that
+	  it only exits when the executing task has really completed.
+
+	  ASTERISK-25355 #close
+
+	  Change-Id: I51198270eb0b637c956c61aa409f46283432be61
+
+2015-08-27 12:26 +0000 [c2c7319082]  Joshua Colp <jcolp at digium.com>
+
+	* res_pjsip_session: Don't invoke session supplements twice for BYE requests.
+
+	  When a BYE request is received the PJSIP invite session implementation
+	  creates and sends a 200 OK response before we are aware of it. This
+	  causes the INVITE session state callback to be called into and ultimately
+	  the session supplements run on the BYE request. Once this response has
+	  been sent the normal transaction state callback is invoked which
+	  invokes the session supplements on the BYE request again. This can
+	  be problematic in particular with res_pjsip_rfc3326 as it may
+	  attempt to update the hangup cause code on the channel while it is
+	  in the process of being hung up.
+
+	  This change makes it so the session supplements are only invoked
+	  once by the INVITE session state callback.
+
+	  ASTERISK-25318 #close
+
+	  Change-Id: I69c17df55ccbb61ef779ac38cc8c6b411376c19a
+
+2015-08-26 15:26 +0000 [6862c2a167]  Scott Griepentrog <scott at griepentrog.com>
+
+	* Chaos: handle failed allocation in get_media_encryption_type
+
+	  If the ast_strndup() call fails to allocate a copy of the
+	  transport string for parsing, fail gracefully.
+
+	  ASTERISK-25323
+	  Reported by: Scott Griepentrog
+
+	  Change-Id: Ia4b905ce6d03da53fea526224455c1044b1a5a28
+
+2015-08-26 14:25 +0000 [f1cd636658]  Scott Griepentrog <scott at griepentrog.com>
+
+	* Chaos: make hangup NULL tolerant
+
+	  In chan_pjsip_new, if allocation of the pvt
+	  structure fails, ast_hangup is called.  But
+	  it was written to assume pvt was valid, and
+	  this change corrects that.
+
+	  ASTERISK-25323
+	  Reported by: Scott Griepentrog
+
+	  Change-Id: I5f47860fe9cee4cd56abd3f79b108678ab72cc87
+
+2015-08-26 05:40 +0000 [c01111223f]  Joshua Colp <jcolp at digium.com>
+
+	* chan_sip: Allow call pickup to set the hangup cause.
+
+	  The call pickup implementation in chan_sip currently sets the channel
+	  hangup cause to "normal clearing" if call pickup is successfully
+	  performed. This action overwrites the "answered elsewhere" hangup cause
+	  set by the call pickup code and can result in the SIP device in
+	  question showing a missed call when it should not.
+
+	  This change sets the hangup cause to "normal clearing" as a
+	  default initially but allows the call pickup to change it as
+	  needed.
+
+	  ASTERISK-25346 #close
+
+	  Change-Id: I00ac2c269cee9e29586ee2c65e83c70e52a02cff
+
+2015-08-25 07:17 +0000 [2a4eee0cd9]  Joshua Colp <jcolp at digium.com>
+
+	* res_pjsip: Add common ast_sip_get_host_ip API.
+
+	  Modules commonly used the pj_gethostip function for retrieving the
+	  IP address of the host. This function does not cache the result and may
+	  result in a DNS lookup occurring, or additional work. If the DNS
+	  server is unreachable or network issues arise this can cause the
+	  pj_gethostip function to block for a period of time.
+
+	  This change adds an ast_sip_get_host_ip and ast_sip_get_host_ip_string
+	  function which does the same thing but caches the host IP address at
+	  module load time. This results in no additional work being done each
+	  time the local host IP address is needed.
+
+	  ASTERISK-25342 #close
+
+	  Change-Id: I3205deb679b01fa5ac05a94b623bfd620a2abe1e
+
+2015-08-24 11:04 +0000 [7c4d0c3506]  Joshua Colp <jcolp at digium.com>
+
+	* res_pjsip_pubsub: On recreated notify fail deleted sub_tree is referenced
+
+	  When recreating a subscription it is possible for a freed sub_tree
+	  to be referenced when the initial NOTIFY fails to be created.
+
+	  Change-Id: I681c215309aad01b21d611c2de47b3b0a6022788
+
+2015-08-24 06:21 +0000 [6c2dab1e88]  Joshua Colp <jcolp at digium.com>
+
+	* bridge: Kick channel from bridge if hung up during action.
+
+	  When executing an action in a bridge it is possible for the
+	  channel to be hung up without the bridge becoming aware of it.
+	  This is most easily reproducible by hanging up when the bridge
+	  is streaming DTMF due to a feature timeout. This change makes
+	  it so after action execution the channel is checked to determine
+	  if it has been hung up and if it has it is kicked from the bridge.
+
+	  ASTERISK-25341 #close
+
+	  Change-Id: I6dd8b0c3f5888da1c57afed9e8a802ae0a053062
+
+2015-08-23 18:26 +0000 [bc6fe07f5c]  Matt Jordan <mjordan at digium.com>
+
+	* res_pjsip/pjsip_configuration: Disregard empty auth values
+
+	  When an endpoint is backed by a non-static conf file backend (such as
+	  the AstDB or Realtime), the 'auth' object may be returned as being an
+	  empty string. Currently, res_pjsip will interpret that as being a valid
+	  auth object, and will attempt to authenticate inbound requests. This
+	  isn't desired; is an auth value is empty (which the name of an auth
+	  object cannot be), we should instead interpret that as being an invalid
+	  auth object and skip it.
+
+	  ASTERISK-25339 #close
+
+	  Change-Id: Ic32b0c6eb5575107d5164a8c40099e687cd722c7
+
+2015-08-19 12:10 +0000 [0582776f7f]  Richard Mudgett <rmudgett at digium.com>
+
+	* ari/ari_websockets.c: Fix ast_debug parameter type mismatch.
+
+	  This is a type mismatch fix of the debugging commit
+	  c63316eec10e1990a88bf4712238d6deb375bfa9 made to find out why
+	  a testsuite test was failing only on one of the continuous
+	  integration build agents.
+
+	  Change-Id: Iba34f6e87cec331f6ac80e4daff6476ea6f00a75
+
+2015-08-19 10:30 +0000 [504213f542]  Scott Griepentrog <scott at griepentrog.com>
+
+	* contrib: script install_prereq should install sqlite3
+
+	  Asterisk needs the sqlite 3 library, which is package
+	  sqlite-devel in CentOS. By adding this package to the
+	  script, a problem with configure failing is resolved.
+
+	  ASTERISK-25331 #close
+	  Reported by: Kevin Harwell
+
+	  Change-Id: I90efaf6a01914fea03f21e5cdbd91c348f44b0ec
+
+2015-08-18 16:06 +0000 [77518d5434]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_http_websocket.c: Fix some off nominal path cleanup.
+
+	  * Remove extraneous unlock on off-nominal path.
+	  * Add missing HTTP error reply.
+
+	  Change-Id: I1f402bfe448fba8696b507477cab5f060ccd9b2b
+
+2015-08-18 14:46 +0000 [c61547fee6]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_ari.c: Add missing off nominal unlock and remove a RAII_VAR().
+
+	  Change-Id: I0c5e7b34057f26dadb39489c4dac3015c52f5dbf
+
+2015-08-17 16:41 +0000 [bd867cd078]  Richard Mudgett <rmudgett at digium.com>
+
+	* app_queue.c: Extract some functions for simpler code.
+
+	  * Extract set_queue_member_pause() from set_member_paused() for simpler
+	  and more consistent code.
+
+	  * Extract set_queue_member_ringinuse() from
+	  set_member_ringinuse_help_members() for simpler code.
+
+	  Change-Id: Iecc1f4119c63347341d7ea6b65f5fc4963706306
+
+2015-08-14 12:55 +0000 [e5f5b9f384]  Richard Mudgett <rmudgett at digium.com>
+
+	* app_queue.c: Fix setting QUEUE_MEMBER 'paused' and 'ringinuse'.
+
+	  Setting the 'paused' and 'ringinuse' options on a queue member using the
+	  dialplan function QUEUE_MEMBER did not behave the same way as the
+	  equivalent dialplan applications or AMI actions.
+
+	  * Made queue_function_mem_write() call the set_member_paused() and
+	  set_member_value() for the 'paused' and 'ringinuse' options respectively.
+	  A beneficial side effect is that the queue name is now optional and sets
+	  the value in all queues the interface is a member.
+
+	  * Update QUEUE_MEMBER XML documentation.
+
+	  * Fix error checking in QUEUE_MEMBER() write.
+
+	  ASTERISK-25215 #close
+	  Reported by: Lorne Gaetz
+
+	  Change-Id: I3a016be8dc94d63a9cc155295ff9c9afa5f707cb
+
+2015-08-17 13:34 +0000 [ded51e3d77]  Richard Mudgett <rmudgett at digium.com>
+
+	* app_queue.c: Fix error checking in QUEUE_MEMBER() read.
+
+	  Change-Id: I7294e13d27875851c2f4ef6818adba507509d224
+
+2015-08-17 11:00 +0000 [ab373f2cef]  Scott Griepentrog <scott at griepentrog.com>
+
+	* CHAOS: prevent sorcery object with null id
+
+	  When allocating a sorcery object, fail if the
+	  id value was not allocated.
+
+	  ASTERISK-25323
+	  Reported by: Scott Griepentrog
+
+	  Change-Id: I152133fb7545a4efcf7a0080ada77332d038669e
+
+2015-08-14 15:46 +0000 [b719f56c72]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip_sdp_rtp: Restore removed NULL check.
+
+	  When sending an RTP keepalive, we need to be sure we're not dealing with
+	  a NULL RTP instance. There had been a NULL check, but the commit that
+	  added the rtp_timeout and rtp_hold_timeout options removed the NULL
+	  check.
+
+	  Change-Id: I2d7dcd5022697cfc6bf3d9e19245419078e79b64
+
+2015-08-13 12:30 +0000 [cea5dc7b8a]  Richard Mudgett <rmudgett at digium.com>
+
+	* audiohook.c: Simplify variable usage in audiohook_read_frame_both().
+
+	  Change-Id: I58bed58631a94295b267991c5b61a3a93c167f0c
+
+2015-08-13 12:22 +0000 [b3a56bee83]  Richard Mudgett <rmudgett at digium.com>
+
+	* audiohook.c: Fix MixMonitor crash when using the r() or t() options.
+
+	  The built frame format in audiohook_read_frame_both() is now set to a
+	  signed linear format before the rx and tx frames are duplicated instead of
+	  only for the mixed audio frame duplication.
+
+	  ASTERISK-25322 #close
+	  Reported by Sean Pimental
+
+	  Change-Id: I86f85b5c48c49e4e2d3b770797b9d484250a1538
+
+2015-08-12 12:59 +0000 [25af2d71c8]  Kevin Harwell <kharwell at digium.com>
+
+	* chan_sip.c: wrong peer searched in sip_report_security_event
+
+	  In chan_sip, after handling an incoming invite a security event is raised
+	  describing authorization (success, failure, etc...). However, it was doing
+	  a lookup of the peer by extension. This is fine for register messages, but
+	  in the case of an invite it may search and find the wrong peer, or a non
+	  existent one (for instance, in the case of call pickup). Also, if the peers
+	  are configured through realtime this may cause an unnecessary database lookup
+	  when caching is enabled.
+
+	  This patch makes it so that sip_report_security_event searches by IP address
+	  when looking for a peer instead of by extension after an invite is processed.
+
+	  ASTERISK-25320 #close
+
+	  Change-Id: I9b3f11549efb475b6561c64f0e6da1a481d98bc4
+2015-08-13 05:26 +0000 [e18c300550]  Joshua Colp <jcolp at digium.com>
+
+	* res_http_websocket: When shutting down a session don't close closed socket
+
+	  Due to the use of ast_websocket_close in session termination it is
+	  possible for the underlying socket to already be closed when the
+	  session is terminated. This occurs when the close frame is attempted
+	  to be written out but fails.
+
+	  Change-Id: I7572583529a42a7dc911ea77a974d8307d5c0c8b
+2015-08-11 05:24 +0000 [b4e9416138]  Joshua Colp <jcolp at digium.com>
+
+	* res_http_websocket: Forcefully terminate on write errors.
+
+	  The res_http_websocket module will currently attempt to close
+	  the WebSocket connection if fatal cases occur, such as when
+	  attempting to write out data and being unable to. When the
+	  fatal cases occur the code attempts to write a WebSocket close
+	  frame out to have the remote side close the connection. If
+	  writing this fails then the connection is not terminated.
+
+	  This change forcefully terminates the connection if the
+	  WebSocket is to be closed but is unable to send the close frame.
+
+	  ASTERISK-25312 #close
+
+	  Change-Id: I10973086671cc192a76424060d9ec8e688602845
+
+2015-08-10 13:43 +0000 [256bc52b66]  Richard Mudgett <rmudgett at digium.com>
+
+	* chan_dahdi.c: Flush the DAHDI write buffer after starting DTMF.
+
+	  Pressing DTMF digits on a phone to go out on a DAHDI channel can result in
+	  the digit not being recognized or even heard by the peer.
+
+	  Phone -> Asterisk -> DAHDI/channel
+
+	  Turns out the DAHDI behavior with DTMF generation (and any other generated
+	  tones) is exposed by the "buffers=" setting in chan_dahdi.conf.  When
+	  Asterisk requests to start sending DTMF then DAHDI waits until its write
+	  buffer is empty before generating any samples for the DTMF tones.  When
+	  Asterisk subsequently requests DAHDI to stop sending DTMF then DAHDI
+	  immediately stops generating the DTMF samples.  As a result, the more
+	  samples there are in the DAHDI write buffer the shorter the time DTMF
+	  actually gets sent on the wire.  If there are more samples in the write
+	  buffer than the time DTMF is supposed to be sent then no DTMF gets sent on
+	  the wire.  With the "buffers=12,half" setting and each buffer representing
+	  20 ms of samples then the DAHDI write buffer is going to contain around
+	  120 ms of samples.  For DTMF to be recognized by the peer the actual sent
+	  DTMF duration needs to be a minimum of 40 ms.  Therefore, the intended
+	  duration needs to be a minimum of 160 ms for the peer to receive the
+	  minimum DTMF digit duration to recognize it.
+
+	  A simple and effective solution to work around the DAHDI behavior is for
+	  Asterisk to flush the DAHDI write buffer when sending DTMF so the full
+	  duration of DTMF is actually sent on the wire.  When someone is going to
+	  send DTMF they are not likely to be talking before sending the tones so
+	  the flushed write samples are expected to just contain silence.
+
+	  * Made dahdi_digit_begin() flush the DAHDI write buffer after requesting
+	  to send a DTMF digit.
+
+	  ASTERISK-25315 #close
+	  Reported by John Hardin
+
+	  Change-Id: Ib56262c708cb7858082156bfc70ebd0a220efa6a
+
+2015-08-05 14:21 +0000 [800e0ea48d]  Richard Mudgett <rmudgett at digium.com>
+
+	* chan_dahdi.c: Lock private struct for ast_write().
+
+	  There is a window of opportunity for DTMF to not go out if an audio frame
+	  is in the process of being written to DAHDI while another thread starts
+	  sending DTMF.  The thread sending the audio frame could be past the
+	  currently dialing check before being preempted by another thread starting
+	  a DTMF generation request.  When the thread sending the audio frame
+	  resumes it will then cause DAHDI to stop the DTMF tone generation.  The
+	  result is no DTMF goes out.
+
+	  * Made dahdi_write() lock the private struct before writing to the DAHDI
+	  file descriptor.
+
+	  ASTERISK-25315
+	  Reported by John Hardin
+
+	  Change-Id: Ib4e0264cf63305ed5da701188447668e72ec9abb
+
+2015-08-10 18:23 +0000 [c126afe18f]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip.c: Fix crash from corrupt saved SUBSCRIBE message.
+
+	  If the saved SUBSCRIBE message is not parseable for whatever reason then
+	  Asterisk could crash when libpjsip tries to parse the message and adds an
+	  error message to the parse error list.
+
+	  * Made ast_sip_create_rdata() initialize the parse error rdata list.  The
+	  list is checked after parsing to see that it remains empty for the
+	  function to return successful.
+
+	  ASTERISK-25306
+	  Reported by Mark Michelson
+
+	  Change-Id: Ie0677f69f707503b1a37df18723bd59418085256
+
+2015-08-10 07:40 +0000 [f68c995bc9]  Alexander Traud <pabstraud at compuserve.com>
+
+	* chan_sip: Fix negotiation of iLBC 30.
+
+	  iLBC 20 was advertised in a SIP/SDP negotiation. However, only iLBC 30 is
+	  supported. Removes "a=fmtp:x mode=y" from SDP. Because of RFC 3952 section 5,
+	  only iLBC 30 is negotiated now.
+
+	  ASTERISK-25309 #close
+
+	  Change-Id: I92d724600a183eec3114da0ac607b994b1a793da
+
+2015-08-09 18:42 +0000 [8e194047ac]  Matt Jordan <mjordan at digium.com>
+
+	* res/res_format_attr_silk: Expose format attributes to other modules
+
+	  This patch adds the .get callback to the format attribute module, such
+	  that the Asterisk core or other third party modules can query for the
+	  negotiated format attributes.
+
+	  Change-Id: Ia24f55cf9b661d651ce89b4f4b023d921380f19c
+
+2015-08-09 17:56 +0000 [a0f451c35e]  Matt Jordan <mjordan at digium.com>
+
+	* main/format: Add an API call for retrieving format attributes
+
+	  Some codecs that may be a third party library to Asterisk need to have
+	  knowledge of the format attributes that were negotiated. Unfortunately,
+	  when the great format migration of Asterisk 13 occurred, that ability
+	  was lost.
+
+	  This patch adds an API call, ast_format_attribute_get, to the core
+	  format API, along with updates to the unit test to check the new API
+	  call. A new callback is also now available for format attribute modules,
+	  such that they can provide the format attribute values they manage.
+
+	  Note that the API returns a void *. This is done as the format attribute
+	  modules themselves may store format attributes in any particular manner
+	  they like. Care should be taken by consumers of the API to check the
+	  return value before casting and dereferencing. Consumers will obviously
+	  need to have a priori knowledge of the type of the format attribute as
+	  well.
+
+	  Change-Id: Ieec76883dfb46ecd7aff3dc81a52c81f4dc1b9e3
+
+2015-08-07 22:11 +0000 [26f0559a94]  David M. Lee <dlee at respoke.io>
+
+	* Replace htobe64 with htonll
+
+	  We don't have a compatability function to fill in a missing htobe64; but
+	  we already have one for the identical htonll.
+
+	  Change-Id: Ic0a95db1c5b0041e14e6b127432fb533b97e4cac
+
+2015-08-07 14:20 +0000 [df9ce36366]  Scott Emidy <jemidy at digium.com>
+
+	* ARI: Retrieve existing log channels
+
+	  An http request can be sent to get the existing Asterisk logs.
+
+	  The command "curl -v -u user:pass -X GET 'http://localhost:8088
+	  /ari/asterisk/logging'" can be run in the terminal to access the
+	  newly implemented functionality.
+
+	  * Retrieve all existing log channels
+
+	  ASTERISK-25252
+
+	  Change-Id: I7bb08b93e3b938c991f3f56cc5d188654768a808
+
+2015-08-07 11:14 +0000 [e9f1bc08cb]  Scott Emidy <jemidy at digium.com>
+
+	* ARI: Creating log channels
+
+	  An http request can be sent to create a log channel
+	  in Asterisk.
+
+	  The command "curl -v -u user:pass -X POST
+	  'http://localhost:088/ari/asterisk/logging/mylog?
+	  configuration=notice,warning'" can be run in the terminal
+	  to access the newly implemented functionality for ARI.
+
+	  * Ability to create log channels using ARI
+
+	  ASTERISK-25252
+
+	  Change-Id: I9a20e5c75716dfbb6b62fd3474faf55be20bd782
+
+2015-08-06 15:18 +0000 [78364132ce]  Scott Emidy <jemidy at digium.com>
+
+	* ARI: Deleting log channels
+
+	  An http request can be sent to delete a log channel
+	  in Asterisk.
+
+	  The command "curl -v -u user:pass -X DELETE 'http://localhost:8088
+	  /ari/asterisk/logging/mylog'" can be run in the terminal
+	  to access the newly implemented functionally for ARI.
+
+	  * Able to delete log channels using ARI
+
+	  ASTERISK-25252
+
+	  Change-Id: Id6eeb54ebcc511595f0418d586ff55914bc3aae6
+
+2015-08-06 12:48 +0000 [e25569ef95]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip_pubsub: More accurately persist packet.
+
+	  The pjsip_rx_data structure has a pkt_info.packet field on it that is
+	  the packet that was read from the transport. For datagram transports,
+	  the packet read from the transport will correspond to the SIP message
+	  that arrived. For streamed transports, however, it is possible to read
+	  multiple SIP messages in one packet.
+
+	  In a recent case, Asterisk crashed on a system where TCP was being used.
+	  This is because at some point, a read from the TCP socket resulted in a
+	  200 OK response as well as an incoming SUBSCRIBE request being stored in
+	  rdata->pkt_info.packet. When the SUBSCRIBE was processed, the
+	  combination 200 OK and SUBSCRIBE was saved in persistent storage. Later,
+	  a restart of Asterisk resulted in the crash because the persistent
+	  subscription recreation code ended up building the 200 OK response
+	  instead of a SUBSCRIBE request, and we attempted to access
+	  request-specific data.
+
+	  The fix here is to use the pjsip_msg_print() function in order to
+	  persist SUBSCRIBE requests. This way, rather than using the raw socket
+	  data, we use the parsed SIP message that PJSIP has given us. If we
+	  receive multiple SIP messages from a single read, we will be sure only
+	  to save off the relevant SIP message. There also is a safeguard put in
+	  place to make sure that if we do end up reconstructing a SIP response,
+	  it will not cause a crash.
+
+	  ASTERISK-25306 #close
+	  Reported by Mark Michelson
+
+	  Change-Id: I4bf16f7b76a2541d10b55de82bcd14c6e542afb2
+
+2015-08-04 16:12 +0000 [8521a86367]  Joshua Colp <jcolp at digium.com>
+
+	* res_pjsip: Ensure sanitized XML is NULL terminated.
+
+	  The ast_sip_sanitize_xml function is used to sanitize
+	  a string for placement into XML. This is done by examining
+	  an input string and then appending values to an output
+	  buffer. The function used by its implementation, strncat,
+	  has specific behavior that was not taken into account.
+	  If the size of the input string exceeded the available
+	  output buffer size it was possible for the sanitization
+	  function to write past the output buffer itself causing
+	  a crash. The crash would either occur because it was
+	  writing into memory it shouldn't be or because the resulting
+	  string was not NULL terminated.
+
+	  This change keeps count of how much remaining space is
+	  available in the output buffer for text and only allows
+	  strncat to use that amount.
+
+	  Since this was exposed by the res_pjsip_pidf_digium_body_supplement
+	  module attempting to send a large message the maximum allowed
+	  message size has also been increased in it.
+
+	  A unit test has also been added which confirms that the
+	  ast_sip_sanitize_xml function is providing NULL terminated
+	  output even when the input length exceeds the output
+	  buffer size.
+
+	  ASTERISK-25304 #close
+
+	  Change-Id: I743dd9809d3e13d722df1b0509dfe34621398302
+
+2015-08-05 05:23 +0000 [9a12804e59]  Joshua Colp <jcolp at digium.com>
+
+	* res_rtp_asterisk: Don't leak temporary key when enabling PFS.
+
+	  A change recently went in which enabled perfect forward secrecy for
+	  DTLS in res_rtp_asterisk. This was accomplished two different ways
+	  depending on the availability of a feature in OpenSSL. The fallback
+	  method created a temporary instance of a key but did not free it.
+	  This change fixes that.
+
+	  ASTERISK-25265
+
+	  Change-Id: Iadc031b67a91410bbefb17ffb4218d615d051396
+2015-08-04 09:47 +0000 [27dc2094e9]  Mark Michelson <mmichelson at digium.com>
+
+	* res_http_websocket: Debug write lengths.
+
+	  Commit 39cc28f6ea2140ad6d561fd4c9e9a66f065cecee attempted to fix a
+	  test failure observed on 32 bit test agents by ensuring that a cast from
+	  a 32 bit unsigned integer to a 64 bit unsigned integer was happening in
+	  a predictable place. As it turns out, this did not cause test runs to
+	  succeed.
+
+	  This commit adds several redundant debug messages that print the payload
+	  lengths of websocket frames. The idea here is that this commit will not
+	  cause tests to succeed for the faulty test agent, but we might deduce
+	  where the fault lies more easily this way by observing at what point the
+	  expected value (537) changes to some ungangly huge number.
+
+	  If you are wondering why something like this is being committed to the
+	  branch, keep in mind that in commit
+	  39cc28f6ea2140ad6d561fd4c9e9a66f065cecee I noted that the observed test
+	  failures only happen when automated tests are run. Attempts to run the
+	  tests by hand manually on the test agent result in the tests passing.
+
+	  Change-Id: I14a65c19d8af40dadcdbd52348de3b0016e1ae8d
+
+2015-08-03 11:06 +0000 [39cc28f6ea]  Mark Michelson <mmichelson at digium.com>
+
+	* res_http_websocket: Avoid passing strlen() to ast_websocket_write().
+
+	  We have seen a rash of test failures on a 32-bit build agent. Commit
+	  48698a5e21d7307f61b5fb2bd39fd593bc1423ca solved an obvious problem where
+	  we were not encoding a 64-bit value correctly over the wire. This
+	  commit, however, did not solve the test failures.
+
+	  In the failing tests, ARI is attempting to send a 537 byte text frame
+	  over a websocket. When sending a frame this small, 16 bits are all that
+	  is required in order to encode the payload length on the websocket
+	  frame. However, ast_websocket_write() thinks that the payload length is
+	  greater than 65535 and therefore writes out a 64 bit payload length.
+	  Inspecting this payload length, the lower 32 bits are exactly what we
+	  would expect it to be, 537 in hex. The upper 32 bits, are junk values
+	  that are not expected to be there.
+
+	  In the failure, we are passing the result of strlen() to a function that
+	  expects a uint64_t parameter to be passed in. strlen() returns a size_t,
+	  which on this 32-bit machine is 32 bits wide. Normally, passing a 32-bit
+	  unsigned value to somewhere where a 64-bit unsigned value is expected
+	  would cause no problems. In fact, in manual runs of failing tests, this
+	  works just fine. However, ast_websocket_write() uses the Asterisk
+	  optional API, which means that rather than a simple function call, there
+	  are a series of macros that are used for its declaration and
+	  implementation. These macros may be causing some sort of error to occur
+	  when converting from a 32 bit quantity to a 64 bit quantity.
+
+	  This commit changes the logic by making existing ast_websocket_write()
+	  calls use ast_websocket_write_string() instead. Within
+	  ast_websocket_write_string(), the 64-bit converted strlen is saved in a
+	  local variable, and that variable is passed to ast_websocket_write()
+	  instead.
+
+	  Note that this commit message is full of speculation rather than
+	  certainty. This is because the observed test failures, while always
+	  present in automated test runs, never occur when tests are manually
+	  attempted on the same test agent. The idea behind this commit is to fix
+	  a theoretical issue by performing changes that should, at the least,
+	  cause no harm. If it turns out that this change does not fix the failing
+	  tests, then this commit should be reverted.
+
+	  Change-Id: I4458dd87d785ca322b89c152b223a540a3d23e67
+
+2015-07-28 05:33 +0000 [aed068844c]  Mark Duncan <mark at syon.co.jp>
+
+	* res/res_rtp_asterisk: Add ECDH support
+
+	  This will add ECDH support to Asterisk. It will
+	  detect auto ECDH support in OpenSSL
+	  (1.0.2b and above) during ./configure. If this is
+	  available, it will use it,
+	  otherwise it will fall back to prime256v1 (this
+	  behavior is consistent with
+	  other projects such as Apache and nginx).
+
+	  This fixes WebRTC being broken in Firefox 38+ due
+	  to Firefox now only supporting
+	  ciphers with perfect forward secrecy.
+
+	  ASTERISK-25265 #close
+
+	  Change-Id: I8c13b33a2a79c0bde2e69e4ba6afa5ab9351465b
+
+2015-07-29 14:17 +0000 [1ae762634c]  Benjamin Ford <bford at digium.com>
+
+	* ARI: Rotate log channels.
+
+	  An http request can be sent to rotate a specified log channel.
+	  If the channel does not exist, an error response will be
+	  returned.
+
+	  The command "curl -v -u user:pass -X PUT 'http://localhost:8088
+	  /ari/asterisk/logging/logChannelName/rotate'" can be run in the
+	  terminal to access this new functionality.
+
+	  * Added the ability to rotate log files through ARI
+
+	  ASTERISK-25252
+
+	  Change-Id: Iaefa21cbbc1b29effb33004ee3d89c977e76ab01
+
+2015-07-29 13:49 +0000 [aeeb170fc4]  Richard Mudgett <rmudgett at digium.com>
+
+	* rtp_engine.c: Fix performance issue with several channel drivers that use RTP.
+
+	  ast_rtp_codecs_get_payload() gets called once or twice for every received
+	  RTP frame so it would be nice to not allocate an ao2 object to then have
+	  it destroyed shortly thereafter.  The ao2 object gets allocated only if
+	  the payload type is not set by the channel driver as a negotiated value.
+	  The issue affects chan_skinny, chan_unistim, chan_rtp, and chan_ooh323.
+
+	  * Made static_RTP_PT[] an array of ao2 objects that
+	  ast_rtp_codecs_get_payload() can return instead of an array of structs
+	  that must be copied into a created ao2 object.
+
+	  ASTERISK-25296 #close
+	  Reported by: Richard Mudgett
+
+	  Change-Id: Icb6de5cd90bfae07d44403a1352963db9109dac0
+
+2015-07-29 17:00 +0000 [84262749d2]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_rtp_asterisk.c: Fix off-nominal crash potential.
+
+	  ASTERISK-25296
+	  Reported by: Richard Mudgett
+
+	  Change-Id: I08549fb7c3ab40a559f41a3940f3732a4059b55b
+
+2015-07-29 13:48 +0000 [1519eb44a7]  Richard Mudgett <rmudgett at digium.com>
+
+	* rtp_engine.c: Must protect mime_types_len with mime_types_lock.
+
+	  Change-Id: I44220dd369cc151ebf5281d5119d84bb9e54d54e
+
+2015-07-24 18:42 +0000 [a93b7a927c]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_sdp_rtp.c: Fix processing wrong SDP media list.
+
+	  Change-Id: I7c076826c2d3c6ae8c923ca73b7a71980cca11f2
+
+2015-07-24 18:38 +0000 [741fa0d26d]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_sdp_rtp.c: Fixup some whitespace.
+
+	  Change-Id: Ib4eb7ef7dcaf93ddc26538f0a498aaf110d7a973
+
+2015-07-27 19:10 +0000 [89b21fd9a3]  Richard Mudgett <rmudgett at digium.com>
+
+	* rtp_engine.h: No sense allowing payload types larger than RFC allows.
+
+	  * Tweaked add_static_payload() to not use magic numbers.
+
+	  Change-Id: I1719ff0f6d3ce537a91572501eae5bcd912a420b
+
+2015-07-23 14:04 +0000 [7427c7f13b]  Richard Mudgett <rmudgett at digium.com>
+
+	* rtp_engine.c: Minor tweaks.
+
+	  * Fix off nominial ref leak of new_type in
+	  ast_rtp_codecs_payloads_set_m_type().
+
+	  * No need to lock static_RTP_PT_lock in
+	  ast_rtp_codecs_payloads_set_m_type() and
+	  ast_rtp_codecs_payloads_set_rtpmap_type_rate() before the payload type
+	  parameter sanity check.
+
+	  * No need to create ast_rtp_payload_type ao2 objects with a lock since the
+	  lock is not used.
+
+	  Change-Id: I64dd1bb4dfabdc7e981e3f61448beac9bb7504d4
+
+2015-07-23 12:41 +0000 [e20f435b60]  Richard Mudgett <rmudgett at digium.com>
+
+	* rtp_engine.h: Misc comment fixes.
+
+	  Change-Id: If98139264d5d97427b4685ecbdc54518f725bc43
+
+2015-07-17 16:23 +0000 [bc5d7f9c37]  Richard Mudgett <rmudgett at digium.com>
+
+	* chan_sip.c: Tweak glue->update_peer() parameter nil value.
+
+	  Change glue->update_peer() parameter from 0 to NULL to better indicate it
+	  is a pointer.
+
+	  Change-Id: I8ff2e5087f0e19f6998e3488a712a2470cc823bd
+
+2015-07-30 17:05 +0000 [13eb491e35]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_session.c: Fix crashes seen when call cancelled.
+
+	  Two testsuite tests crashed in the same place as a result of an INVITE
+	  being CANCELed.
+
+	  tests/channels/pjsip/resolver/srv/failover/in_dialog/transport_unspecified
+	  tests/channels/pjsip/resolver/srv/failover/in_dialog/transport_tcp
+
+	  The session pointer is no longer in the inv->mod_data[session_module.id]
+	  location because the INVITE transaction has reached the terminated state.
+
+	  ASTERISK-25297 #close
+	  Reported by: Richard Mudgett
+
+	  Change-Id: Idb75fdca0321f5447d5dac737a632a5f03614427
+
+2015-07-29 14:35 +0000 [48698a5e21]  Mark Michelson <mmichelson at digium.com>
+
+	* res_http_websocket: Properly encode 64 bit payload
+
+	  A test agent was continuously failing all ARI tests when run against
+	  Asterisk 13. As it turns out, the reason for this is that on those test
+	  runs, for some reason we decided to use the super extended 64 bit
+	  payload length for websocket text frames instead of the extended 16 bit
+	  payload length. For 64-bit payloads, the expected byte order over the
+	  network is
+
+	  7, 6, 5, 4, 3, 2, 1, 0
+
+	  However, we were sending the payload as
+
+	  3, 2, 1, 0, 7, 6, 5, 4
+
+	  This meant that we were saying to expect an absolutely MASSIVE payload
+	  to arrive. Since we did not follow through on this expected payload
+	  size, the client would sit patiently waiting for the rest of the payload
+	  to arrive until the test would time out.
+
+	  With this change, we use the htobe64() function instead of htonl() so
+	  that a 64-bit byte-swap is performed instead of a 32 bit byte-swap.
+
+	  Change-Id: Ibcd8552392845fbcdd017a8c8c1043b7fe35964a
+
+2015-07-29 12:23 +0000 [10ba72a927]  Mark Michelson <mmichelson at digium.com>
+
+	* Add a test event for inband ringing.
+
+	  This event is necessary for the bridge_wait_e_options test to be able to
+	  confirm that ringing is being played on the local channel that runs the
+	  BridgeWait() application with the e(r) option.
+
+	  ASTERISK-25292 #close
+	  Reported by Kevin Harwell
+
+	  Change-Id: Ifd3d3d2bebc73344d4b5310d0d55c7675359d72e
+
+2015-07-16 12:16 +0000 [8458b8d441]  Jonathan Rose <jrose at digium.com>
+
+	* holding_bridge: ensure moh participants get frames
+
+	  Currently, if a blank musiconhold.conf is used, musiconhold will fail
+	  to start for a channel going into a holding bridge with an anticipation
+	  of getting music on hold. That being the case, no frames will be written
+	  to the channel and that can pose a problem for blind transfers in PJSIP
+	  which may rely on frames being written to get past the REFER framehook.
+	  This patch makes holding bridges start a silence generator if starting
+	  music on hold fails and makes it so that if no music on hold functions
+	  are installed that the ast_moh_start function will report a failure so
+	  that consumers of that function will be able to respond appropriately.
+
+	  ASTERISK-25271 #close
+
+	  Change-Id: I06f066728604943cba0bb0b39fa7cf658a21cd99
+
+2015-07-24 22:20 +0000  Asterisk Development Team <asteriskteam at digium.com>
+
+	* asterisk 13.5.0-rc1 Released.
+
+2015-07-24 17:15 +0000 [a4b527393b]  Matt Jordan <mjordan at digium.com>
+
+	* Release summaries: Add summaries for 13.5.0-rc1
+
+2015-07-24 17:11 +0000 [158b0b8ebf]  Matt Jordan <mjordan at digium.com>
+
+	* .version: Update for 13.5.0-rc1
+
+2015-07-24 17:11 +0000 [a0a7650e34]  Matt Jordan <mjordan at digium.com>
+
+	* .lastclean: Update for 13.5.0-rc1
+
+2015-07-24 17:11 +0000 [4d238af086]  Matt Jordan <mjordan at digium.com>
+
+	* realtime: Add database scripts for 13.5.0-rc1
+
+2015-07-24 12:56 +0000 [f78a4b52b8]  Matt Jordan <mjordan at digium.com>
+
+	* Bump the ARI version to 1.8.0
+
+	  Due to backwards compatible changes, the ARI version should be bumped to
+	  1.8.0 prior to the release of 13.5.0. Note that a previous patch already
+	  bumped the version of AMI for this release.
+
+	  Change-Id: I419033bfbbc0d3533a29ccb32b2981f39e0883e7
+
+2015-07-18 11:16 +0000 [2749721791]  Joshua Colp <jcolp at digium.com>
+
+	* pjsip: Add rtp_timeout and rtp_timeout_hold endpoint options.
+
+	  This change adds support for the 'rtp_timeout' and 'rtp_timeout_hold'
+	  endpoint options. These allow the channel to be hung up if RTP
+	  is not received from the remote endpoint for a specified number of
+	  seconds.
+
+	  ASTERISK-25259 #close
+
+	  Change-Id: I3f39daaa7da2596b5022737b77799d16204175b9
+
+2015-07-24 09:46 +0000 [b4e19e414a]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip: Add rtp_keepalive to sample config file.
+
+	  Change-Id: I5f62d0c5684f8b2335f9f8ac2d79ee04fbdafb19
+
+2015-07-23 13:11 +0000 [f635520527]  Mark Michelson <mmichelson at digium.com>
+
+	* Local channels: Alternate solution to ringback problem.
+
+	  Commit 54b25c80c8387aea9eb20f9f4f077486cbdf3e5d solved an issue where a
+	  specific scenario involving local channels and a native local RTP bridge
+	  could result in ringback still being heard on a calling channel even
+	  after the call is bridged.
+
+	  That commit caused many tests in the testsuite to fail with alarming
+	  consequences, such as not sending DialBegin and DialEnd events, and
+	  giving incorrect hangup causes during calls.
+
+	  This commit reverts the previous commit and implements and alternate
+	  solution. This new solution involves only passing AST_CONTROL_RINGING
+	  frames across local channels if the local channel is in AST_STATE_RING.
+	  Otherwise, the frame does not traverse the local channels. By doing
+	  this, we can ensure that a playtones generator does not get started on
+	  the calling channel but rather is started on the local channel on which
+	  the ringing frame was initially indicated.
+
+	  ASTERISK-25250 #close
+	  Reported by Etienne Lessard
+
+	  Change-Id: I3bc87a18a38eb2b68064f732d098edceb5c19f39
+
+2015-07-22 12:24 +0000 [f509730cb9]  Joshua Colp <jcolp at digium.com>
+
+	* audiohook: Use manipulated frame instead of dropping it.
+
+	  Previous changes to sample rate support in audiohooks accidentally
+	  removed code responsible for allowing the manipulate audiohooks
+	  to work. Without this code the manipulated frame would be dropped
+	  and not used. This change restores it.
+
+	  ASTERISK-25253 #close
+
+	  Change-Id: I3ff50664cd82faac8941f976fcdcb3918a50fe13
+
+2015-07-22 09:46 +0000 [54b25c80c8]  Mark Michelson <mmichelson at digium.com>
+
+	* Local channels: Do not block control -1 payloads.
+
+	  Control frames with a -1 payload are used as a special signal to stop
+	  playtones generators on channels. This indication is sent both by
+	  app_dial as well as by ast_answer() when a call is answered in case any
+	  tones were being generated on a calling channel.
+
+	  This control frame type was made to stop traversing local channel pairs
+	  as an optimization, because it was thought that it was unnecessary to
+	  send these indications, and allowing such unnecessary control frames to
+	  traverse the local channels would cause the local channels to optimize
+	  away less quickly.
+
+	  As it turns out, through some special magic dialplan code, it is
+	  possible to have a tones being played on a non-local channel, and it is
+	  important for the local channel to convey that the tones should be
+	  stopped. The result of having tones continue to be played on the
+	  non-local channel is that the tones play even once the channel has been
+	  bridged. By not blocking the -1 control frame type, we can ensure that
+	  this situation does not happen.
+
+	  ASTERISK-25250 #close
+	  Reported by Etienne Lessard
+
+	  Change-Id: I0bcaac3d70b619afdbd0ca8a8dd708f33fd2f815
+
+2015-07-22 05:16 +0000 [f1493f900e]  Joshua Colp <jcolp at digium.com>
+
+	* audiohook: Read the correct number of samples based on audiohook format.
+
+	  Due to changes in audiohooks to support different sample rates the
+	  underlying storage of samples is in the format of the audiohook
+	  itself and not of the format being requested. This means that if a
+	  channel is using G722 the samples stored will be at 16kHz. If
+	  something subsequently reads from the audiohook at a format which
+	  is not the same sample rate as the audiohook the number of samples
+	  needs to be adjusted.
+
+	  Given the following example:
+	  1. Channel writing into audiohook at 16kHz (as it is using G722).
+	  2. Chanspy reading from audiohook at 8kHz.
+
+	  The original code would read 160 samples from the audiohook for
+	  each 20ms of audio. This is incorrect. Since the audio in the
+	  audiohook is at 16kHz the actual number needing to be read is 320.
+	  Failure to read this much would cause the audiohook to reset
+	  itself constantly as the buffer became full.
+
+	  This change adjusts the requested number of samples by determining
+	  the duration of audio requested and then calculating how many
+	  samples that would be in the audiohook format.
+
+	  ASTERISK-25247 #close
+
+	  Change-Id: Ia91ce516121882387a315fd8ee116b118b90653d
+
+2015-07-20 12:39 +0000 [62c64c3bd1]  Rusty Newton <rnewton at digium.com>
+
+	* Documentation: A couple of trivial fixes in sip.conf.sample and func_cdr.c
+
+	   * In sip.conf.sample fix sentence where we said that WS or WSS are supported
+	     transports for use in an outbound register definition. They are not
+	     supported in that case.
+	   * In func_cdr.c made it clear that the Disable option for CDR_PROP can be used
+	     to enable CDR on a channel.
+
+	  ASTERISK-24867 #close
+	  Reported by: Rusty Newton
+
+	  ASTERISK-24853 #close
+	  Reported by: PSDK
+
+	  Change-Id: I3d698bc6302b9d00a0a995b5c4ad9a42d69b48ca
+
+2015-07-09 14:17 +0000 [d9094ddd73]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip: Add rtp_keepalive endpoint option.
+
+	  This adds an "rtp_keepalive" option for PJSIP endpoints. Similar to the
+	  chan_sip option, this specifies an interval, in seconds, at which we
+	  will send RTP comfort noise frames. This can be useful for keeping RTP
+	  sessions alive as well as keeping NAT associations alive during lulls.
+
+	  ASTERISK-25242 #close
+	  Reported by Mark Michelson
+
+	  Change-Id: I06660ba672c0a343814af4cec838e6025cafd54b
+
+2015-07-16 09:13 +0000 [a23adcca3d]  Michael Cargile <mikec at vicidial.com>
+
+	* res/res_musiconhold: Add a warning when MOH does not exist
+
+	  Change-Id: Ifdfbd0b97cf31478d29923ec30aabce28d01740b
+
+2015-07-19 09:11 +0000 [03064daeb2]  Matt Jordan <mjordan at digium.com>
+
+	* res/res_sorcery_config: Prevent crash from misconfigured sorcery.conf
+
+	  Misconfiguring sorcery.conf with a 'config' wizard with no extra data
+	  will currently crash Asterisk on startup, as the wizard requires a comma
+	  delineated list to parse. This patch updates res_sorcery_config to check
+	  for the presence of the data before it starts manipulating it.
+
+	  Change-Id: I4c97512e8258bc82abe190627a9206c28f5d3847
+
+2015-07-16 09:46 +0000 [2c626ceb64]  Joshua Colp <jcolp at digium.com>
+
+	* chan_pjsip: Don't change formats when frame of unsupported format is received.
+
+	  Receipt of an RTP packet currently causes the formats on an PJSIP channel to
+	  change to the format of the RTP packet. In some off-nominal cases it's possible
+	  for this to be a format that has not been configured or negotiated. This change
+	  makes it so only formats explicitly configured on the endpoint are allowed.
+
+	  ASTERISK-25258 #close
+
+	  Change-Id: If93d641fb6418a285928839300d7854cab8c1020
+
+2015-07-17 04:59 +0000 [abb14ac5b8]  Patric Marschall <patric.marschall at 1und1.de>
+
+	* sig_pri.h: force_restart_unavailable_chans in wrong scope
+
+	  In channels/sig_pri.h, struct sig_pri_span, the field
+	  force_restart_unavailable_chans is only defined if
+
+	  #if defined(HAVE_PRI_MCID) is true.
+
+	  All other occurences of force_restart_unavailable_chans are outside of the
+
+	  #if defined(HAVE_PRI_MCID)
+	  endif
+
+	  scope.
+
+	  ASTERISK-25257 #close
+	  Reported by: Patric Marschall
+
+	  Change-Id: I071de89cc2cd0d85927a013036e235851f672549
+2015-07-14 16:55 +0000 [875aee4c09]  Richard Mudgett <rmudgett at digium.com>
+
+	* pbx.c: Post AMI VarSet event if delete a non-empty dialplan variable.
+
+	  ASTERISK-25256 #close
+	  Reported by: Richard Mudgett
+
+	  Change-Id: I0b6be720b66fa956f6a798cd22ef8934eb0c0ff3
+
+2015-07-08 16:39 +0000 [8bcf6d2801]  Matt Jordan <mjordan at digium.com>
+
+	* ARI: Add support for push configuration of dynamic object
+
+	  This patch adds support for push configuration of dynamic, i.e.,
+	  sorcery, objects in Asterisk. It adds three new REST API calls to the
+	  'asterisk' resource:
+	   * GET /asterisk/{configClass}/{objectType}/{id}: retrieve the current
+	     object given its ID. This returns back a list of ConfigTuples, which
+	     define the fields and their present values that make up the object.
+	   * PUT /asterisk/{configClass}/{objectType}/{id}: create or update an
+	     object. A body may be passed with the request that contains fields to
+	     populate in the object. The same format as what is retrieved using
+	     the GET operation is used for the body, save that we specify that the
+	     list of fields to update are contained in the "fields" attribute.
+	   * DELETE /asterisk/{configClass}/{objectType}/{id}: remove a dynamic
+	     object from its backing storage.
+
+	  Note that the success/failure of these operations is somewhat
+	  configuration dependent, i.e., you must be using a sorcery wizard that
+	  supports the operation in question. If a sorcery wizard does not support
+	  the create or delete mechanisms, then the REST API call will fail with a
+	  403 forbidden.
+
+	  ASTERISK-25238 #close
+
+	  Change-Id: I28cd5c7bf6f67f8e9e437ff097f8fd171d30ff5c
+
+2015-07-15 15:40 +0000 [e31cb6b248]  Richard Mudgett <rmudgett at digium.com>
+
+	* strings.h: Fix issues with escape string functions.
+
+	  Fixes for issues with the ASTERISK-24934 patch.
+
+	  * Fixed ast_escape_alloc() and ast_escape_c_alloc() if the s parameter is
+	  an empty string.  If it were an empty string the functions returned NULL
+	  as if there were a memory allocation failure.  This failure caused the AMI
+	  VarSet event to not get posted if the new value was an empty string.
+
+	  * Fixed dest buffer overwrite potential in ast_escape() and
+	  ast_escape_c().  If the dest buffer size is smaller than the space needed
+	  by the escaped s parameter string then the dest buffer would be written
+	  beyond the end by the nul string terminator.  The num parameter was really
+	  the dest buffer size parameter so I renamed it to size.
+
+	  * Made nul terminate the dest buffer if the source string parameter s was
+	  an empty string in ast_escape() and ast_escape_c().
+
+	  * Updated ast_escape() and ast_escape_c() doxygen function description
+	  comments to reflect reality.
+
+	  * Added some more unit test cases to /main/strings/escape to cover the
+	  empty source string issues.
+
+	  ASTERISK-25255 #close
+	  Reported by: Richard Mudgett
+
+	  Change-Id: Id77fc704600ebcce81615c1200296f74de254104
+
+2015-07-14 14:29 +0000 [243c0d1609]  Richard Mudgett <rmudgett at digium.com>
+
+	* parking_applications.c: Fix ast_verb() line terminator.
+
+	  Change-Id: I8797238c71563e243c48c6145b4f1ae58f91f775
+
+2015-07-14 14:36 +0000 [c782320c68]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_parking: Fix crash if ATTENDEDTRANSFER set empty before Park.
+
+	  setup_park_common_datastore() was assuming that a non-NULL string returned
+	  for the ATTENDEDTRANSFER and BLINDTRANSFER channel variables are not empty
+	  strings.  Things got crashy as a result.
+
+	  * Made setup_park_common_datastore() treat the channel variable values the
+	  same whether they are NULL or empty for ATTENDEDTRANSFER and
+	  BLINDTRANSFER.
+
+	  ASTERISK-25254 #close
+	  Reported by: Richard Mudgett
+
+	  Change-Id: I9a9c174b33f354f35f82cc6b7cea8303adbaf9c2
+
+2015-07-10 18:01 +0000 [2735dd5b2d]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_session.c: Extract sip_session_defer_termination_stop_timer().
+
+	  Change-Id: I9e115dee74bd72e06081d0ee73ecdeb886caa5fb
+
+2015-07-10 10:42 +0000 [3d0ca343ca]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_session.c: Add some helpful comments and minor tweaks.
+
+	  Change-Id: I742aeeaf5f760593f323a00fb691affe22e35743
+
+2015-07-10 10:43 +0000 [8d08bb179c]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_session.c: Fix off nominal crash potential in debug message.
+
+	  Change-Id: I09928297927ee85f7655289acee3a586816466bc
+
+2015-07-15 10:31 +0000 [0a1a550593]  Matt Jordan <mjordan at digium.com>
+
+	* apps/app_dictate: Fix typo in attribution
+
+	  Last time I checked, it's "Sangoma", not "Samgoma". Thanks to Brian
+	  (GameGamer43) for pointing that out.
+
+	  Change-Id: I43d7b196f6d7a2b2517b84915e3a8dfbc2894106
+
+2015-07-15 10:28 +0000 [3384e64ef6]  Benjamin Ford <bford at digium.com>
+
+	* ARI: Fixed unload mode for unload module.
+
+	  Changed the unload mode to AST_FORCE_SOFT from AST_FORCE_FIRM,
+	  which would unload a module even if it was in use.
+
+	  * Changed unload mode to proper mode
+
+	  ASTERISK-25173
+
+	  Change-Id: If2402487b5bce05d9770f25f65f5c8e292ad5533
+
+2015-07-08 16:38 +0000 [0b6ff77afb]  Matt Jordan <mjordan at digium.com>
+
+	* res/res_sorcery_astdb: Add a debugging message for when retrieval by ID fails
+
+	  Having a debug message tell us that we attempted to look up an item but
+	  failed is nice in circumstances when it isn't clear if the wizard was
+	  queried correctly or not.
+
+	  Change-Id: I2600c3bbea87f252196358f62e73f4c7da8632f7
+
+2015-07-08 16:37 +0000 [2f0d6d346c]  Matt Jordan <mjordan at digium.com>
+
+	* res/res_pjsip_outbound_registration: Fix WARNING message
+
+	  Newlines are nice.
+
+	  Change-Id: Icf0d915db02882e47cd9077ed9009f5d44140d42
+
+2015-07-08 16:35 +0000 [cd2213f1ae]  Matt Jordan <mjordan at digium.com>
+
+	* res_pjsip/configuration: Fix a variety of default value problems
+
+	  This patch fixes some bad default value handling in the following
+	  settings:
+
+	  * The 'message_context' and 'accountcode' settings are not mandatory. As
+	    such, we can allow their stringfield values to be empty.
+	  * The 'media_encryption' setting applies a default value of 'none' to
+	    the setting, which it then can't parse or understand. Since the value
+	    is documented to be 'no', this will now apply that as the default
+	    value.
+
+	  Change-Id: Ib9be7f97a7a5b9bc7aee868edf5acf38774cff83
+
+2015-07-08 16:32 +0000 [2e4bdbd78a]  Matt Jordan <mjordan at digium.com>
+
+	* main/sorcery: Provide log messages when a wizard does not support an operation
+
+	  If a sorcery wizard does not support one of the 'optional' CRUD
+	  operations (namely the CUD), log a WARNING message so we are aware of
+	  why the operation failed. This also removes an assert in this case, as
+	  the CUD operation may have been triggered by an external system, in
+	  which case it is not a programming error but a configuration error.
+
+	  Change-Id: Ifecd9df946d9deaa86235257b49c6e5e24423b53
+
+2015-07-10 18:17 +0000 [653f2087e0]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_session.c: Fix crash on call disconnect.
+
+	  The crash fix for ASTERISK-25183 backported some code from master to try
+	  to make sure that a BYE response is processed by the same serializer used
+	  by the BYE request.  The identified race condition causing that backport
+	  was the BYE request code had not finished processing after sending the BYE
+	  before the BYE response came in for processing under a different thread.
+	  Unfortunately, there is still a race condition.  Now the race condition is
+	  between destroying the call session's serializer in
+	  ast_taskprocessor_unreference() and using ast_taskprocessor_get() to get a
+	  reference to the serializer for a BYE response.  Even worse, the new race
+	  condition is a design limitation of the taskprocessor implementation that
+	  didn't matter in versions before v12.  Back then, taskprocessors were only
+	  destroyed when a module unloaded.  Now res_pjsip can destroy them when a
+	  call ends.
+
+	  However, as noted on the ASTERISK-25183 commit,
+	  session_inv_on_state_changed() is disassociating the dialog from the
+	  session when the invite dialog state becomes PJSIP_INV_STATE_DISCONNECTED.
+	  This is a tad too soon because our BYE request transaction has not
+	  completed yet.
+
+	  * Split session_end() that is called by session_inv_on_state_changed() to
+	  hold off session destruction until the BYE transaction timeout occurs or a
+	  failed initial INVITE transaction timeout occurs in
+	  session_inv_on_tsx_state_changed().
+
+	  ASTERISK-25201 #close
+	  Reported by: Matt Jordan
+
+	  Change-Id: Iaf8dc8485fd8392a2a3ee4ad3b7f7f04a0dcc961
+
+2015-07-14 13:12 +0000 [1aafadf814]  Benjamin Ford <bford at digium.com>
+
+	* ARI: Added new functionality to reload a single module.
+
+	  An http request can be sent to reload an Asterisk module. If the
+	  module can not be reloaded or is not already loaded, an error
+	  response will be returned.
+
+	  The command "curl -v -u user:pass -X PUT 'http://localhost:8088
+	  /ari/asterisk/modules/{moduleName}'" (or something similar, based
+	  on configuration) can be run in the terminal to access this new
+	  functionality.
+
+	  For more information, see:
+	  https://wiki.asterisk.org/wiki.display/~bford/Asterisk+ARI+Resource
+
+	  * Added new ARI functionality
+	  * Asterisk modules can be reloaded through http requests
+
+	  ASTERISK-25173
+
+	  Change-Id: I289188bcae182b2083bdbd9ebfffd50b62f58ae1
+
+2015-07-14 08:55 +0000 [9dcae23cfc]  Benjamin Ford <bford at digium.com>
+
+	* ARI: Added new functionality to unload a single module.
+
+	  An http request can be sent to unload an Asterisk module. If the
+	  module can not be unloaded or is already unloaded, an error response
+	  will be returned.
+
+	  The command "curl -v -u user:pass -X DELETE 'http://localhost:8088
+	  /ari/asterisk/modules/{moduleName}'" (or something similar, depending
+	  on configuration) can be run in the terminal to access this new
+	  functionality.
+
+	  For more information, see:
+	  https://wiki.asterisk.org/wiki.display/~bford/Asterisk+ARI+Resource
+
+	  * Added new ARI functionality
+	  * Asterisk modules can be unloaded through http requests
+
+	  ASTERISK-25173
+
+	  Change-Id: I535a95f5676deb02651522761ecbdc0b00b5ac57
+
+2015-07-13 16:00 +0000 [c219a98d2b]  Benjamin Ford <bford at digium.com>
+
+	* ARI: Added new functionality to load a single module.
+
+	  An http request can be sent to load an Asterisk module. If the
+	  module can not be loaded or is loaded already, an error response
+	  will be returned.
+
+	  The command curl -v -u user:pass -X POST 'http://localhost:8088/ari
+	  /asterisk/modules/{moduleName}'" (or something similar, depending on
+	  configuration) can be run in the terminal to access this new
+	  functionality.
+
+	  For more information, see:
+	  https://wiki.asterisk.org/wiki.display/~bford/Asterisk+ARI+Resource
+
+	  * Added new ARI functionality
+	  * Asterisk modules can be loaded through http requests
+
+	  ASTERISK-25173
+
+	  Change-Id: I9e05d5b8c5c666ecfef341504f9edc1aa84fda33
+
+2015-07-13 10:54 +0000 [73e35d20de]  Benjamin Ford <bford at digium.com>
+
+	* ARI: Added new functionality to get information on a single module.
+
+	  An http request can be sent to retrieve information on a single
+	  module, including the resource name, description, use count, status,
+	  and support level.
+
+	  The command "curl -v -u user:pass -X GET 'http://localhost:8088/ari
+	  /asterisk/modules/{moduleName}'" (or something similar, depending on
+	  configuration) can be run in the terminal to access this new
+	  functionality.
+
+	  For more information, see:
+	  https://wiki.asterisk.org/wiki.display/~bford/Asterisk+ARI+Resource
+
+	  * Added new ARI functionality
+	  * Information on a single module can now be retrieved
+
+	  ASTERISK-25173
+
+	  Change-Id: Ibce5a94e70ecdf4e90329cf0ba66c33a62d37463
+
+2015-07-08 14:56 +0000 [97ee0ee6c6]  Kevin Harwell <kharwell at digium.com>
+
+	* bridge.c: Fixed race condition during attended transfer
+
+	  During an attended transfer a thread is started that handles imparting the
+	  bridge channel. From the start of the thread to when the bridge channel is
+	  ready exists a gap that can potentially cause problems (for instance, the
+	  channel being swapped is hung up before the replacement channel enters the
+	  bridge thus stopping the transfer). This patch adds a condition that waits
+	  for the impart thread to get to a point of acceptable readiness before
+	  allowing the initiating thread to continue.
+
+	  ASTERISK-24782
+	  Reported by: John Bigelow
+
+	  Change-Id: I08fe33a2560da924e676df55b181e46fca604577
+
+2015-07-08 16:28 +0000 [bb76b88baf]  Matt Jordan <mjordan at digium.com>
+
+	* main/sorcery: Don't fail object set creation from JSON if field fails
+
+	  Some individual fields may fail their conversion due to their default
+	  values being invalid for their custom handlers. In particular,
+	  configuration values that depend on others being enabled (and thus have
+	  an empty default value) are notorious for tripping this routine up. An
+	  example of this are any of the DTLS options for endpoints. Any of the
+	  DTLS options will fail to be applied (as DTLS is not enabled), causing
+	  the entire object set to be aborted.
+
+	  This patch makes it so that we log a debug message when skipping a
+	  field, and rumble on anyway.
+
+	  ASTERISK-25238
+
+	  Change-Id: I0bea13de79f66bf9f9ae6ece0e94a2dc1c026a76
+
+2015-07-08 16:21 +0000 [5f13c2226a]  Matt Jordan <mjordan at digium.com>
+
+	* main/format_cap: Parse capabilities generated by ast_format_cap_get_names
+
+	  We have a strange relationship between the parsing of format
+	  capabilities from a string and their representation as a string. We
+	  expect the format capabilities to be expressed as a string in the
+	  following format:
+
+	  allow = !all,ulaw,alaw
+	  disallow = g722
+
+	  While we would generate the string representation of those formats as:
+
+	  allow = (ulaw|alaw)
+	  disallow = (ulaw|alaw|g729...)
+
+	  When the configuration framework needs to store values as a string, it
+	  generates the format capabilities using the second representation; this
+	  representation however cannot be parsed when the entry is rehydrated.
+	  This patch fixes that by updating
+	  ast_format_cap_update_by_allow_disallow to parse an entry as if it were
+	  in the generated format if it has a leading '(' and a trailing ')'.
+
+	  ASTERISK-25238
+
+	  Change-Id: I904d43caf4cf45af06f6aee0c9e58556eb91d6ca
+
+2015-06-27 17:53 +0000 [2325b106fd]  Matt Jordan <mjordan at digium.com>
+
+	* tests/test_devicestate: Add additional tests for the device state API
+
+	  This patch adds more tests that exercise the device state API. This includes:
+
+	  * Tests that cover adding a device state provider, as well as deleting a
+	    device state provider. This also verifies that you cannot add an
+	    already added device state provider, and cannot delete an already
+	    deleted device state provider.
+	  * A test that covers changing device state and receiving said updates
+	    from a device state subscriber. This also covers hitting both the
+	    device state cache as well as a custom device state provider.
+	  * A test that covers converting device state to channel state and device
+	    state values to a string representation and back.
+	  * A test that covers obtaining device state from an active channel and a
+	    channel driver that provides its own device state.
+
+	  Change-Id: I2adca67ffb405cd8625a5d6df1e3f9b3d945c08d
+
+2015-06-27 17:51 +0000 [328f0be806]  Matt Jordan <mjordan at digium.com>
+
+	* main/devicestate: Prevent duplicate registration of device state providers
+
+	  Currently, the device state provider API will allow you to register a
+	  device state provider with the same case insensitive name more than
+	  once. This could cause strange issues, as the duplicate device state
+	  providers will not be queried when a device's state has to be polled.
+	  This patch updates the API such that a device state provider with the
+	  same name as one that has already registered will be rejected.
+
+	  Change-Id: I4a418a12280b7b6e4960bd44f302e27cd036ceb2
+
+2015-07-10 22:25 +0000 [bee41eec62]  Matt Jordan <mjordan at digium.com>
+
+	* res/res_sorcery_memory_cache: Fix test registration issues
+
+	  Again, tests now need to not end with a newline. This patch makes it so
+	  the tests can register again, unit tests will actually pass, and we can
+	  stop wasting time trying to figure out why builds are failing when they
+	  really aren't failing.
+
+	  Change-Id: Ide519fbeba89f413c733446c5ff7b224fc4ce840
+
+2015-07-10 21:42 +0000 [4d738e9026]  Matt Jordan <mjordan at digium.com>
+
+	* tests/test_sorcery_memory_cache_thrash: Fix test loading problems
+
+	  Because unit tests now want descriptions to not end with a newline, the
+	  sorcery memory cache thrash tests failed to register. This patch
+	  corrects their descriptions.
+
+	  Change-Id: Id004b1becfdeed8ee3c846f49beab76a5c0f68b6
+
+2015-06-26 10:57 +0000 [47ea312b24]  Benjamin Ford <bford at digium.com>
+
+	* ARI: Added new functionality to get all module information.
+
+	  An http request can be sent to retrieve a list of all existing modules,
+	  including the resource name, description, use count, status, and
+	  support level.
+
+	  The command "curl -v -u user:pass -X GET 'http://localhost:8088/ari/
+	  asterisk/modules" (or something similar, depending on configuration)
+	  can be run in the terminal to access this new functionality.
+
+	  For more information, see:
+	  https://wiki.asterisk.org/wiki.display/~bford/Asterisk+ARI+Resource
+
+	  * Added new ARI functionality
+	  * Information on modules can now be retrieved
+
+	  Change-Id: I63cbbf0ec0c3544cc45ed2a588dceabe91c5e0b0
+
+2015-07-09 09:18 +0000 [d558b00c85]  Joshua Colp <jcolp at digium.com>
+
+	* bridge_native_rtp.c: Don't start native RTP bridging after attended transfer.
+
+	  The bridge_native_rtp module adds a frame hook to channels which are in
+	  a native RTP bridge. This frame hook is used to intercept when a hold
+	  or unhold frame traverses the bridge so native RTP can be stopped or
+	  started as appropriate. This is expected but exposes a specific bug
+	  when attended transfers are involved.
+
+	  Upon completion of an attended transfer an unhold frame is queued up
+	  to take one of the channels involved off hold. After this is done
+	  the channel is moved between bridges.
+
+	  When the frame hook is involved in this case for the unhold it
+	  releases the channel lock and acquires the bridge lock. This
+	  allows the bridge core to step in and move the channel
+	  (potentially changing the bridging techology) from another thread.
+	  Once completed the bridge lock is released by the bridge core.
+	  The frame hook is then able to acquire the bridge lock and
+	  wrongfully starts native RTP again, despite the channel no longer
+	  being in the bridge or needing to start native RTP. In fact at
+	  this point the frame hook is no longer attached to the channel.
+
+	  This change makes it so the native RTP bridge data is available to
+	  the frame hook when it is invoked. Whether the frame hook has
+	  been detached or not is stored on the native RTP bridge data and
+	  is checked by the frame hook before starting or stopping native
+	  RTP bridging. If the frame hook has been detached it does nothing.
+
+	  ASTERISK-25240 #close
+
+	  Change-Id: I13a73186a05f4e5a764f81e5cd0ccec1ed1891d2
+
+2015-05-16 17:02 +0000 [b74b071369]  Joshua Colp <jcolp at digium.com>
+
+	* res_sorcery_memory_cache: Backport to 13
+
+	  Gerrit is complaining of conflicts when trying to create a patch series
+	  of all of the cherry-picked master commits, so I have instead squashed
+	  it all into one commit.
+
+	  ASTERISK-25067 #close
+	  Reported by: Matt Jordan
+
+	  Change-Id: I6dda90343fae24a75dc5beec84980024e8d61eb9
+
+2015-07-08 04:21 +0000 [7ff1ac8797]  Joshua Colp <jcolp at digium.com>
+
+	* res_rtp_asterisk: Ensure DTLS timeout timer is -1 if DTLS is not used.
+
+	  This change fixes a bug where the DTLS timeout timer would be
+	  initialized to 0 if DTLS was not used for an RTP session.
+
+	  ASTERISK-25103
+
+	  Change-Id: If8d26bb054f1d300838850da5b8db9044c2fe2ac
+
+2015-07-01 07:55 +0000 [05e8e14982]  Joshua Colp <jcolp at digium.com>
+
+	* res_rtp_asterisk: Prevent simultaneous access to DTLS SSL context.
+
+	  This change moves logic for setting up the DTLS SSL contexts to
+	  when the SDP is done being processed instead of when ICE negotiation
+	  completes. It also stops handshakes from being initiated when we
+	  are acting as a server.
+
+	  Manipulating the SSL context when ICE negotiation has completed
+	  is problematic as the SSL context is not protected and if acting
+	  as a client the remote side may have started DTLS negotiation
+	  already.
+
+	  The retransmission timeout timer code has also been split up
+	  and simplified some. Both RTP and RTCP now have their own timers
+	  and the points at which the timer is stopped and started is now
+	  more specific. When a packet is sent the timer is started. When
+	  a response is received but before it is processed the timer is
+	  stopped. This provides a guarantee that the timeout is not
+	  occurring while the response is processed.
+
+	  ASTERISK-22805 #close
+	  ASTERISK-24550 #close
+	  ASTERISK-24651 #close
+	  ASTERISK-24832 #close
+	  ASTERISK-25103 #close
+	  ASTERISK-25127 #close
+
+	  Change-Id: Ib75ea2546f29d6efc3d2d37c58df6986c7bd9b91
+
+2015-06-26 16:10 +0000 [38bace4fbb]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_t38.c: Fix always false if test.
+
+	  Calling t38_change_state() sets the t38 state so it makes little sense to
+	  then check the state right after the call for something else.
+
+	  * Made the code in t38_interpret_parameters() reject or exit T.38 mode as
+	  intended but not implemented.
+
+	  Change-Id: Ib281263a6ed44da9448132c4e6df1e183b8a3df2
+
+2015-06-30 11:17 +0000 [2f7688c788]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_mwi.c: Use safer loop coding in mwi_subscription_mailboxes_str().
+
+	  Change-Id: I6f39d809a6d1b47b35bb32b298f5a12f35d6f907
+
+2015-06-30 11:14 +0000 [74be3a50d7]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_mwi.c: Eliminate a simple RAII_VAR.
+
+	  Change-Id: Ib1843f81e826a6c760c424c88eb70c350d9d61da
+
+2015-06-30 11:11 +0000 [589e93617a]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_mwi.c: Fix mid-line log message line breaks.
+
+	  * Add create_mwi_subscriptions_for_endpoint() doxygen comment.
+
+	  Change-Id: I3c3f921f4ec749fb65b62d2f6fa0d4d1888b94e2
+
+2015-06-26 18:48 +0000 [0d67e04359]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_mwi.c: Fix MWI subscription memory corruption crash.
+
+	  MWI subscriptions can crash or corrupt memory when using the subscription
+	  datastore to access the MWI subscription object because the datastore is
+	  not holding a reference to the object.
+
+	  * Give the subscription datastore a ref to the MWI subscription object.
+	  It is unfortunate that the ref causes a circular ref chain that must be
+	  explicitly broken to allow the memory to get released.  The loop is broken
+	  when the subscription is shutdown and if the subscription setup fails.
+
+	  ASTERISK-25168 #close
+	  Reported by: Carl Fortin
+
+	  Change-Id: Ice4fa823f138ff10a6c74d280699c41a82836d4f
+
+2015-07-02 14:51 +0000 [0422433f47]  Richard Mudgett <rmudgett at digium.com>
+
+	* PJSIP XML, XPIDF: Fix buffer size overwrite memory corruption error.
+
+	  When res_pjsip body generator modules were generating XML or XPIDF
+	  response bodies, there was a chance that the generated body would be the
+	  exact size of the supplied buffer.  Adding the nul string terminator would
+	  then write beyond the end of the buffer and potentially corrupt memory.
+
+	  * Fix MALLOC_DEBUG high fence violations caused by adding a nul string
+	  terminator on the end of a buffer for XML or XPIDF response bodies.
+
+	  * Made calls to pj_xml_print() safer if the XML prolog is requested.  Due
+	  to a bug in pjproject, the return value could be -1 _or_
+	  AST_PJSIP_XML_PROLOG_LEN if the supplied buffer is not large enough.
+
+	  * Updated the doxygen comment of AST_PJSIP_XML_PROLOG_LEN to describe the
+	  return value of pj_xml_print() when the supplied buffer is not large
+	  enough.
+
+	  ASTERISK-25168
+	  Reported by: Carl Fortin
+
+	  Change-Id: Id70e1d373a6a2b2bd9e678b5cbc5e55b308981de
+
+2015-06-26 10:36 +0000 [8ea214aed7]  Richard Mudgett <rmudgett at digium.com>
+
+	* PJSIP FAX: Fix T.38 automatic reject timer NULL channel pointer dereferences.
+
+	  When a caller calls a FAX number and then hangs up right after the call is
+	  answered then the T.38 re-INVITE automatic reject timer may still be
+	  running after the channel goes away.
+
+	  * Added session NULL channel checks on the code paths that get executed by
+	  t38_automatic_reject() to prevent a crash when the T.38 re-INVITE
+	  automatic reject timer expires.
+
+	  ASTERISK-25168
+	  Reported by: Carl Fortin
+
+	  Change-Id: I07b6cd23815aedce5044f8f32543779e2f7a2403
+
+2015-06-05 15:37 +0000 [ada7346792]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip: Need to use the same serializer for a pjproject SIP transaction.
+
+	  All send/receive processing for a SIP transaction needs to be done under
+	  the same threadpool serializer to prevent reentrancy problems inside
+	  pjproject and res_pjsip.
+
+	  * Add threadpool API call to get the current serializer associated with
+	  the worker thread.
+
+	  * Pick a serializer from a pool of default serializers if the caller of
+	  res_pjsip.c:ast_sip_push_task() does not provide one.
+
+	  This is a simple way to ensure that all outgoing SIP request messages are
+	  processed under a serializer.  Otherwise, any place where a pushed task is
+	  done that would result in an outgoing out-of-dialog request would need to
+	  be modified to supply a serializer.  Serializers from the default
+	  serializer pool are picked in a round robin sequence for simplicity.
+
+	  A side effect is that the default serializer pool will limit the growth of
+	  the thread pool from random tasks.  This is not necessarily a bad thing.
+
+	  * Made pjsip_distributor.c save the thread's serializer name on the
+	  outgoing request tdata struct so the response can be processed under the
+	  same serializer.
+
+	  This is a cherry-pick from master.
+
+	  **** ASTERISK-25115 Change-Id: Iea71c16ce1132017b5791635e198b8c27973f40a
+
+	  NOTE: session_inv_on_state_changed() is disassociating the dialog from the
+	  session when the invite dialog becomes PJSIP_INV_STATE_DISCONNECTED.
+	  Unfortunately this is a tad too soon because our BYE request transaction
+	  has not completed yet.
+
+	  ASTERISK-25183 #close
+	  Reported by: Matt Jordan
+
+	  Change-Id: I8bad0ae1daf18d75b8c9e55874244b7962df2d0a
+
+2015-07-04 18:22 +0000 [55137c3d12]  Joshua Colp <jcolp at digium.com>
+
+	* res/res_http_websocket: Don't send HTTP response fragmented.
+
+	  This change makes it so that when accepting a WebSocket
+	  connection the HTTP response is sent as one packet instead of
+	  fragmented. Browsers don't like it when you send it fragmented.
+
+	  ASTERISK-25103
+
+	  Change-Id: I9b82c4ec2949b0bce692ad0bf6f7cea9709e7f69
+
+2015-06-27 18:47 +0000 [49f81ddb85]  Matt Jordan <mjordan at digium.com>
+
+	* Makefile: Remove coverage files on 'make clean'
+
+	  This patch updates a variety of Makefiles in Asterisk's build system to
+	  remove .gcda and .gcno files when 'make clean' is executed. These files
+	  are generated when '--enable-coverage' is passed to the Asterisk
+	  configure script.
+
+	  Change-Id: Ib70b41eea2ee2908885bff02e80faf9f40c84602
+
+2015-07-02 09:08 +0000 [e0f565663b]  Walter Doekes <walter+asterisk at wjd.nu>
+
+	* chan_sip: Fix early call pickup channel leak.
+
+	  When handle_invite_replaces() was called, and either ast_bridge_impart()
+	  failed or there was no bridge (because the channel we're picking up was
+	  still ringing), chan_sip would leak a channel.
+
+	  Thanks Matt and Corey for checking the bridge path.
+
+	  ASTERISK-25226 #close
+
+	  Change-Id: Ie736bb182170a73eef5bcef0ab0376f645c260c8
+
+2015-07-02 06:19 +0000 [a5a262be78]  Walter Doekes <walter+asterisk at wjd.nu>
+
+	* chan_mgcp: Don't call close on fd -1.
+
+	  ASTERISK-25220 #close
+
+	  Change-Id: Ic48f3a82f51ada87f2fb0e016c9efe0ad56f1ee3
+
+2015-07-02 06:10 +0000 [b835312b4c]  Walter Doekes <walter+asterisk at wjd.nu>
+
+	* rtp_engine: Skip useless self-assignment in ast_rtp_engine_unload_format.
+
+	  When running valgrind on Asterisk, it complained about:
+
+	      ==32423== Source and destination overlap in memcpy(0x85a920, 0x85a920, 304)
+	      ==32423==    at 0x4C2F71C: memcpy@@GLIBC_2.14 (in /usr/lib/valgrind/...)
+	      ==32423==    by 0x55BA91: ast_rtp_engine_unload_format (rtp_engine.c:2292)
+	      ==32423==    by 0x4EEFB7: ast_format_attr_unreg_interface (format.c:1437)
+
+	  The code in question is a struct assignment, which may be performed by
+	  memcpy as a compiler optimization. It is changed to only copy the struct
+	  contents if source and destination are different.
+
+	  ASTERISK-25219 #close
+
+	  Change-Id: I6d3546c326b03378ca8e9b8cefd41c16e0088b9a
+
+2015-07-02 05:16 +0000 [6551e16e03]  Walter Doekes <walter+asterisk at wjd.nu>
+
+	* astfd: Fix buffer overflow in DEBUG_FD_LEAKS.
+
+	  If DEBUG_FD_LEAKS was used and more file descriptors than the default of
+	  1024 were available, some DEBUG_FD_LEAKS-patched functions would
+	  overwrite memory past the fixed-size (1024) fdleaks buffer.
+
+	  This change:
+	  - adds bounds checks to __ast_fdleak_fopen and __ast_fdleak_pipe
+	  - consistently uses ARRAY_LEN() instead of sizeof() or 1023 or 1024
+	  - stores pointers to constants instead of copying the contents
+	  - reorders the fdleaks struct for possibly tighter packing
+	  - adds a tiny bit of documentation
+
+	  ASTERISK-25212 #close
+
+	  Change-Id: Iacb69e7701c0f0a113786bd946cea5b6335a85e5
+
+2015-07-02 04:57 +0000 [f4dd9560cf]  Walter Doekes <walter+asterisk at wjd.nu>
+
+	* res_timing: Don't close FD 0 when out of open files.
+
+	  This fixes so a failure to get a timer file descriptor does not cascade
+	  to closing FD 0.
+
+	  On error, both res_timing_kqueue and res_timing_timerfd would call the
+	  destructor before setting the file handle. The file handle had been
+	  initialized to 0, causing FD 0 to be closed. This in turn, resulted in
+	  floods of "CLI>" messages and an unusable terminal.
+
+	  ASTERISK-19277 #close
+	  Reported by: Barry Chern
+
+	  For the 13 branch, this was already fixed. This patch only ensures that
+	  we do not attempt to close a negative file descriptor.
+
+	  Change-Id: I147d7e33726c6e5a2751928d56561494f5800350
+
+2015-07-01 17:25 +0000 [78a1f4aa46]  Richard Mudgett <rmudgett at digium.com>
+
+	* chan_vpb.cc: Fix compiler warning Jenkins found.
+
+	  Change-Id: I0ec7fd10d56d90d5a60b12b5a7d6807f265ac5e0
+
+2015-07-01 13:34 +0000 [6b16fbfc22]  Scott Griepentrog <scott at griepentrog.com>
+
+	* Channel alert pipe: improve diagnostic error return
+
+	  When a frame is queued on a channel, any failure in
+	  ast_channel_alert_write is logged along with errno.
+
+	  This change improves the diagnostic message through
+	  aligning the errno value with actual failure cases.
+
+	  ASTERISK-25224
+	  Reported by: Andrey Biglari
+
+	  Change-Id: I1bf7b3337ad392789a9f02c650589cd065d20b5b
+
+2015-07-01 16:04 +0000 [8e07ab145d]  Matt Jordan <mjordan at digium.com>
+
+	* sorcery/realtime: Add a bit of debug and warning messages for bad configs
+
+	  When a mapping does not exist between a sorcery.conf defined object and
+	  a realtime mapping in extconf, currently, the user will receive a slew
+	  of ERROR messages that don't really tell what is happening. Some ERROR
+	  messages may even be misleading, as they occur after the sorcery API has
+	  already given up on the attempt to load and create the sorcery object.
+
+	  This patch adds a bit of debug and a useful WARNING message for when a
+	  wizard's open callback fails for a particular object type. In the bad
+	  configurations that resulted in this patch, this provided a 'root cause'
+	  WARNING message that pointed in the right direction of the configuration
+	  problem.
+
+	  Change-Id: I1cc7344f2b015b8b9c85a7e6ebc8cb4753a8f80b
+2015-06-29 12:45 +0000 [156395e743]  Mark Michelson <mmichelson at digium.com>
+
+	* res_sorcery_realtime: Fix leak of sorcery object type.
+
+	  This prevents a leak of a sorcery object type when realtime sorcery
+	  objects are retrieved by fields or when multiple objects are retrieved.
+
+	  The extent of this leak is that sorcery object types would be leaked.
+	  These are allocated whenever an object type is registered with sorcery,
+	  meaning that on module shutdown, these objects would be leaked. This
+	  could be problematic if many reloads were performed, but it is not as
+	  severe as if every sorcery object retrieved from realtime were being
+	  leaked.
+
+	  ASTERISK-25165 #close
+	  Reported by Corey Farrell
+
+	  Change-Id: I625c3b50eee4576670b7eeb013c81ad043b4b4f8
+
+2015-06-26 22:02 +0000 [a5e9c4e9b2]  Matt Jordan <mjordan at digium.com>
+
+	* res/res_corosync: Always decline module load, instead of failing
+
+	  Returns a 'failure' from the module load routine indicates to Asterisk
+	  that it should abort loading completely. This is rarely - in fact,
+	  really, never - a good option. Aborting load of Asterisk from a dynamic
+	  module implies that the core, and the rest of the dynamic modules, don't
+	  matter: we should abandon all processing.
+
+	  res_corosync is really not that important.
+
+	  This patch updates the module such that, if it fails to load, it
+	  politely declines (emitting ERROR messages along the way), and allows
+	  Asterisk to continue to function.
+
+	  Note that this issue was keeping Asterisk unit tests from running on
+	  certain build agents.
+
+	  Change-Id: I252249e81fb9b1a68e0da873f54f47e21d648f0f
+
+2015-06-26 20:38 +0000 [399cd8bcd9]  Matt Jordan <mjordan at digium.com>
+
+	* main/pbx: Resolve case sensitivity regression in PBX hints
+
+	  When 8297136f was merged for ASTERISK-25040, a regression was introduced
+	  surrounding the case sensitivity of device names within hints.
+	  Previously, device names - such as 'sip/foo' - were compared in a case
+	  insensitive fashion. Thus, 'sip/foo' was equivalent to 'SIP/foo'. After
+	  that patch, only the case sensitive name would match, i.e., 'SIP/foo'.
+	  As a result, some dialplan hints stopped working.
+
+	  This patch re-introduces case insensitive matching for device names in
+	  hints.
+
+	  ASTERISK-25040
+
+	  ASTERISK-25202 #close
+
+	  Change-Id: If5046a7d14097e1e3c12b63092b9584bb1e9cb4c
+	  (cherry picked from commit 96bbcf495a1da9e607d9b04a44b5c4f49e83cc03)
+
+2015-06-26 16:12 +0000 [24eec5a10b]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip_nat: Adjust when contact should be rewritten.
+
+	  A previous change made the contact only get rewritten if the dialog's
+	  route set was not marked frozen. Unfortunately, while the intent of this
+	  is correct, the dialog's route set actually gets marked as frozen
+	  earlier than expected, especially for UAS dialogs.
+
+	  Instead, the idea is that the contact needs to not be rewritten if there
+	  is a pre-existing route set on the dialog. This is now accomplished by
+	  checking the dialog's route set list instead of checking if the route
+	  set is frozen.
+
+	  Doing this causes some broken tests to begin passing again.
+
+	  ASTERISK-25196
+	  Reported by Mark Michelson
+
+	  Change-Id: I525ab251fd40a52ede327a52a2810a56deb0529e
+
+2015-06-19 18:27 +0000 [0ec461a637]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_outbound_registration.c: Add a serializer shutdown group.
+
+	  The client_state objects contain a serializer used to send the outbound
+	  REGISTER messages.  Once all those message transactions are complete then
+	  the module can shutdown.
+
+	  ASTERISK-24907 #close
+	  Reported by: Kevin Harwell
+
+	  Change-Id: Ibb2fe558f98190f2a06da830e0fadfa25516f547
+
+2015-06-26 10:41 +0000 [05a2cc1293]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip_refer: Prevent sending duplicate headers.
+
+	  res_pjsip_refer will attempt to add Referred-By or Replaces headers to
+	  outbound INVITEs at times. If the INVITE gets challenged for
+	  authentication, then we will resend the INVITE. Prior to this patch, the
+	  Referred-By or Replaces header would be re-added to the outbound INVITE,
+	  resulting in duplicated headers.
+
+	  ASTERISK-25204 #close
+	  Reported by Mark Michelson
+
+	  Change-Id: I59fb5c08b4d253c0dba9ee3d3950b5025358222d
+
+2015-06-23 17:43 +0000 [028fa54620]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip_nat: Rewrite route set when required.
+
+	  When performing some provider testing, the rewrite_contact option was
+	  interfering with proper construction of a route set when sending an ACK
+	  after receiving a 200 OK response to an INVITE.
+
+	  The initial INVITE was sent to address sip:foo. The 200 OK had a Contact
+	  header with URI sip:bar. In addition, the 200 OK had Record-Route
+	  headers for sip:baz and sip:foo, in that order. Since the Record-Route
+	  headers had the lr parameter, the result should have been:
+
+	  * Set R-URI of the ACK to sip:bar.
+	  * Add Route headers for sip:foo and sip:baz, in that order.
+
+	  However, the rewrite_contact option resulted in our rewriting the
+	  Contact header on the 200 OK to sip:foo. The result was:
+
+	  * R-URI remained sip:foo.
+	  * We added Route headers for sip:foo and sip:baz, in that order.
+
+	  The result was that sip:bar was not indicated in the ACK at all, so the
+	  far end never received our ACK. The call eventually dropped.
+
+	  The intention of rewrite_contact is to rewrite the most immediate
+	  destination of our SIP request to be the same address on which we
+	  received a request or response. In the case of processing a SIP response
+	  with Record-Route headers, this means that instead of rewriting the
+	  Contact header, we should instead rewrite the bottom-most Record-Route
+	  header. In the case of processing a SIP request with Record-Route
+	  headers, this means we rewrite the top-most Record-route header.
+	  Like when we rewrite the Contact header, we also ensure to update
+	  the dialog's route set if it exists.
+
+	  ASTERISK-25196 #close
+	  Reported by Mark Michelson
+
+	  Change-Id: I9702157c3603a2d0bd8a8215ac27564d366b666f
+2015-06-19 16:16 +0000 [84c12f9e0c]  Richard Mudgett <rmudgett at digium.com>
+
+	* threadpool, res_pjsip: Add serializer group shutdown API calls.
+
+	  A module trying to unload needs to wait for all serializers it creates and
+	  uses to complete processing before unloading.
+
+	  ASTERISK-24907
+	  Reported by: Kevin Harwell
+
+	  Change-Id: I8c80b90f2f82754e8dbb02ddf3c9121e5e966059
+
+2015-06-16 15:06 +0000 [602c4b74b5]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_outbound_registration.c: Fix handle_client_state_destruction() refs
+
+	  * handle_client_state_destruction() must always be passed a ref to
+	  client_state because it will always unref client_state.
+	  handle_registration_response() was not passing a client_state ref.
+
+	  * Made the final un-REGISTER message get sent normally using the pjproject
+	  register control structure in handle_client_state_destruction().  The
+	  previous code attempted to short circuit the response handling for the
+	  module to unload.  That doesn't work for a couple reasons.  One,
+	  pjsip_regc_send() may call the registered callback before it returns and
+	  unbalance the client_state ref count.  Two, the registered callback
+	  handles any authentication for the un-REGISTER message.
+
+	  * Made the distinction between internal registration state and external
+	  registration status with sip_outbound_registration_status_str().  This is
+	  necessary to avoid altering documented AMI messages with internal
+	  changes.
+
+	  * Removed references to client_state->client outside of the serializer
+	  thread.  When handle_client_state_destruction() destroys the pjproject
+	  register control structure that memory is freed and cannot be referenced
+	  anymore.  These accesses were to provide information for debug and
+	  off-nominal warning messages.
+
+	  * In sip_outbound_registration_timer_cb() you should not access entry->id
+	  after unrefing client_state because the passed in entry is normally
+	  pointing to the timer entry in the client_state object.
+
+	  ASTERISK-24907
+	  Reported by: Kevin Harwell
+
+	  Change-Id: Ia7b446d8644b6b4550ef5bea49527671de65183f
+
+2015-06-15 15:28 +0000 [8c6a95a9ac]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_outbound_registration.c: Use ast_sorcery_object_unregister() API
+
+	  The sorcery pjsip 'registration' config object needs to be destroyed on
+	  module unload.  Otherwise, a reload of res_pjsip could try to use
+	  callbacks for a previously unloaded instance of the module provided by
+	  ast_sorcery_object_register() or one of the variants.  Also, if
+	  res_pjsip_outbound_registration were subsequently reloaded, the sorcery
+	  config field objects would be registered in sorcery twice.
+
+	  ASTERISK-24907
+	  Reported by: Kevin Harwell
+
+	  Change-Id: I304fad13dece2604af48353f6c6d9d5c7b064697
+
+2015-06-25 06:42 +0000 [e4a2ef9e4e]  Joshua Colp <jcolp at digium.com>
+
+	* channel: Remove ignore of answer on non-outgoing channels.
+
+	  Due to the way that channels can now be moved around inside of
+	  Asterisk it is possible for the outgoing flag of a channel to get
+	  cleared before it has been answered. This results in the bridge
+	  not receiving notification that the outgoing leg has been answered.
+
+	  This most easily exhibits itself with DTMF based blond transfers.
+	  Since the answer of the outgoing leg is ignored the other party
+	  continues to receive both a locally generated ringing and the
+	  media stream of the outgoing leg upon its answer. This results
+	  in no media being heard.
+
+	  This change removes the ignore of the answer and allows it
+	  to pass through.
+
+	  ASTERISK-25171 #close
+
+	  Change-Id: I82aedcec4f89f34a2e5472086dfc9a6c775bca8e
+
+2015-06-15 15:28 +0000 [20f3d77ab9]  Richard Mudgett <rmudgett at digium.com>
+
+	* sorcery: Add ast_sorcery_object_unregister() API call.
+
+	  Find and unlink the specified sorcery object type to complement
+	  ast_sorcery_object_register().  Without this function you cannot
+	  completely unload individual modules that use sorcery for configuration.
+
+	  ASTERISK-24907
+	  Reported by: Kevin Harwell
+
+	  Change-Id: I1c04634fe9a90921bf676725c7d6bb2aeaab1c88
+
+2015-06-15 13:38 +0000 [4313f32969]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_outbound_registration.c: Reorder load_module() and unload_module().
+
+	  It is best if the loading code creates and initializes the module's
+	  infrastructure before letting the system know of its existence.  The
+	  unloading code needs to reverse the actions of the loading code and in the
+	  reverse order.
+
+	  ASTERISK-24907
+	  Reported by: Kevin Harwell
+
+	  Change-Id: I5d151383e9787b5b60aa5e1627b10f040acdded4
+
+2015-06-23 14:34 +0000 [890c923786]  Richard Mudgett <rmudgett at digium.com>
+
+	* AMI: Add Linkedid to the standard channel snapshot AMI event headers.
+
+	  * The AMI version is bumped to 2.8.0.
+
+	  ASTERISK-25189 #close
+	  Reported by: John Hardin
+
+	  Change-Id: I2b1778c3fdc1dca0ed55db4e3a639eddfb16c2ac
+
+2015-06-24 14:30 +0000 [2602a7484b]  Richard Mudgett <rmudgett at digium.com>
+
+	* test.c: Add unit test registration checks for summary and description.
+
+	  Added checks when a unit test is registered to see that the summary and
+	  description strings do not end with a new-line '\n' for consistency.
+
+	  The check generates a warning message and will cause the
+	  /main/test/registrations unit test to fail.
+
+	  * Updated struct ast_test_info member doxygen comments.
+
+	  Change-Id: I295909b6bc013ed9b6882e85c05287082497534d
+
+2015-06-24 14:39 +0000 [2b0482d699]  Richard Mudgett <rmudgett at digium.com>
+
+	* Unit tests: Fix unit test description strings.
+
+	  Analyzing the code shows that the unit test summary and description
+	  strings should not end with a new-line character.  Where these strings are
+	  used in the code a new-line is provided for output.
+
+	  Change-Id: I129284f5e7ca93d82532334076da4c462d3d9fba
+
+2015-06-23 11:21 +0000 [e99e654d75]  Joshua Colp <jcolp at digium.com>
+
+	* app_dial: Hold reference to calling channel formats when dialing outbound.
+
+	  Currently when requesting a channel the native formats of the
+	  calling channel are provided to the core for usage when dialing
+	  the outbound channel. This occurs without holding the channel lock
+	  or keeping a reference to the formats. This is problematic as
+	  the channel driver may end up changing the formats during this time.
+	  In the case of chan_sip this happens when an SDP negotiation
+	  completes.
+
+	  This change makes it so app_dial keeps a reference to the native
+	  formats of the calling channel which guarantees that they will
+	  remain valid for the period of time needed.
+
+	  ASTERISK-25172 #close
+
+	  Change-Id: I2f0a67bd0d5d14c3bdbaae552b4b1613a283f0db
+2015-06-17 05:04 +0000 [80e82dc97f]  Joshua Colp <jcolp at digium.com>
+
+	* res_pjsip_mwi: Set up unsolicited MWI upon registration.
+
+	  The res_pjsip_mwi previously required a reload to set up the proper
+	  subscriptions to allow unsolicited MWI to work. This change
+	  makes it so the act of registering will also cause this to occur.
+	  This is particularly useful if realtime is involved as no reload
+	  needs to occur within Asterisk to cause the MWI information
+	  to get sent.
+
+	  ASTERISK-25180 #close
+
+	  Change-Id: Id847b47de4b8b3ab8858455ccc2f07b0f915f252
+
+2015-06-22 15:11 +0000 [35a99b6394]  Kevin Harwell <kharwell at digium.com>
+
+	* bridge.c: Hangup attended transfer target if bridged
+
+	  After completing an attended transfer the transfer target channel was not being
+	  hung up after leaving the bridge. Added an explicit softhangup to hangup said
+	  channel, but only if it was previously bridged.
+
+	  ASTERISK-24782 #close
+	  Reported by: John Bigelow
+
+	  Change-Id: Idde9543d56842369384a5e8c00d72a22bbc39ada
+
+2015-06-17 16:23 +0000 [036bc0012f]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_outbound_registration.c: Add missing line endings to CLI commands
+
+	  Change-Id: I39ae612746d892d2dbe86f3ff2d7027fa1da57f7
+
+2015-06-12 14:29 +0000 [bec7435945]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_outbound_registration.c: Eliminate simple RAII_VAR() usage.
+
+	  Change-Id: I399cb9d61bbba706b48c98e0bf75e98984cd9a9e
+
+2015-06-12 13:33 +0000 [c2519fdf1c]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_outbound_registration.c: Misc code cleanups.
+
+	  * Break some long lines.
+
+	  * Fix doxygen comment.
+
+	  Change-Id: I8f12ba6822f84d5e7bb575280270cd7e2fefb305
+
+2015-06-22 09:26 +0000 [a419c69def]  Alexander Traud (License 6520)
+
+	* chan_sip: Reload peer without its old capabilities.
+
+	  On reload, previously allowed codecs were not removed. Therefore, it was not
+	  possible to remove codecs while Asterisk was running. Furthermore, newly added
+	  codecs got appended behind the previous codecs. Therefore, it was not possible
+	  to add a codec with a priority of #1. This change removes the old capabilities
+	  before the current ones are added.
+
+	  ASTERISK-25182 #close
+	  Reported by: Alexander Traud
+	  patches:
+	   asterisk_13_allow_codec_reload.patch uploaded by Alexander Traud (License 6520)
+
+	  Change-Id: I62a06bcf15e08e8c54a35612195f97179ebe5802
+
+2015-06-20 19:38 +0000 [74616ae43d]  Joshua Colp <jcolp at digium.com>
+
+	* chan_sip: Destroy peers without holding peers container lock.
+
+	  Due to the use of stasis_unsubscribe_and_join in the peer destructor
+	  it is possible for a deadlock to occur when an event callback is
+	  occurring at the same time.
+
+	  This happens because the peer may be destroyed while holding the
+	  peers container lock. If this occurs the event callback will never
+	  be able to acquire the container lock and the unsubscribe will
+	  never complete.
+
+	  This change makes it so the peers that have been removed from the
+	  peers container are not destroyed with the container lock held.
+
+	  ASTERISK-25163 #close
+
+	  Change-Id: Ic6bf1d9da4310142a4d196c45ddefb99317d9a33
+
+2015-06-18 13:16 +0000 [9015bb4c8c]  Mark Michelson <mmichelson at digium.com>
+
+	* Resolve race conditions involving Stasis bridges.
+
+	  This resolves two observed race conditions.
+
+	  First, a bit of background on what the Stasis application does:
+
+	  1a Creates a stasis_app_control structure. This structure is linked into
+	     a global container and can be looked up using a channel's unique ID.
+	  2a Puts the channel in an event loop. The event loop can exit either
+	     because the stasis_app_control structure has been marked done, or
+	     because of some other factor, such as a hangup. In the event loop, the
+	     stasis_app_control determines if any specific ARI commands need to be
+	     run on the channel and will run them from this thread.
+	  3a Checks if the channel is bridged. If the channel is bridged, then
+	     ast_bridge_depart() is called since channels that are added to Stasis
+	     bridges are always imparted as departable.
+	  4a Unlink the stasis_app_control from the container.
+
+	  When an ARI command is received by Asterisk, the following occurs
+	  1b A thread is spawned to handle the HTTP request
+	  2b The stasis_app_control(s) that corresponds to the channel(s) in the
+	     request is/are retrieved. If the stasis_app_control cannot be
+	     retrieved, then it is assumed that the channel in question has exited
+	     the Stasis app or perhaps was never in Stasis in the first place.
+	  3b A command is queued onto the stasis_app_control, and the channel's
+	     event loop thread is signaled to run the command.
+	  4b While most ARI commands do nothing further, some, such as adding or
+	     removing channels from a bridge, will block until the command they
+	     issued has been completed by the channel's event loop.
+
+	  The first race condition that is solved by this patch involves a crash
+	  that can occur due to faulty detection of the channel's bridged status
+	  in step 3a. What can happen is that in step 2a, the event loop may run
+	  the ast_bridge_impart() function to asynchronously place the channel
+	  into a bridge, then immediately exit the event loop because the channel
+	  has hung up. In step 3a, we would detect that the channel was not
+	  bridged and would not call ast_bridge_depart(). The reason that the
+	  channel did not appear to be bridged was that the depart_thread that is
+	  spawned by ast_bridge_impart() had not yet started. That is the thread
+	  where the channel is marked as being bridged. Since we did not call
+	  ast_bridge_depart(), the Stasis application would exit, and then the
+	  channel would be destroyed Then the depart_thread would start up and
+	  try to manipulate the destroyed channel, causing a crash.
+
+	  The fix for this is to switch from using ast_channel_is_bridged() to
+	  checking the NULLity of ast_channel_internal_bridge_channel() to
+	  determine if ast_bridge_depart() needs to be called. The channel's
+	  internal bridge_channel is set when ast_bridge_impart() is called and
+	  is NULLed by the call to ast_bridge_depart(). If the channel's internal
+	  bridge_channel is non-NULL, then the channel must have been imparted
+	  into the bridge and needs to be departed, even if the actual bridging
+	  operation has not yet started. By departing the channel when necessary,
+	  the thread that is running the Stasis application will block until the
+	  bridge gives the okay that the depart_thread has exited.
+
+	  The second race condition that is solved by this patch involves a leak
+	  of HTTP handler threads. The problem was that step 2b would successfully
+	  retrieve a stasis_app_control structure. Then step 2a would exit the
+	  channel from the event loop due to a hangup. Steps 3a and 4a would
+	  execute, and then finally steps 3b and 4b would. The problem is that at
+	  step 4b, when attempting to add a channel to a bridge, the thread would
+	  block forever since the channel would never execute the queued command
+	  since it was finished with the event loop. This meant that the HTTP
+	  handling thread would be leaked, along with any references that thread
+	  may have owned (in my case, I was seeing bridges leaked).
+
+	  The fix for this is to hone in better on when the channel has exited the
+	  event loop. The stasis_app_control structure has an is_done field that
+	  is now set at each point where the channel may exit the event loop. If
+	  step 2b retrieves a valid stasis_app_control structure but the control
+	  is marked as done, then the attempted operation exits immediately since
+	  there will be nothing to service the attempted command.
+
+	  ASTERISK-25091 #close
+	  Reported by Ilya Trikoz
+
+	  Change-Id: If66265b73b4c9f8f58599124d777fedc54576628
+2015-06-16 11:13 +0000 [723a9d4225]  Mark Michelson <mmichelson at digium.com>
+
+	* Parking: Add documentation for AMI ParkedCallSwap event.
+
+	  This event was added some time ago in order to clarify when a channel
+	  took the place of another channel in a parking lot. However, there was
+	  no XML documentation added for the event. This patch adds the XML
+	  documentation.
+
+	  ASTERISK-24900 #close
+	  Reported by Rusty Newton
+
+	  Change-Id: I4cfe7777c4b94bbff91c9221c6096a7a02a92eac
+2015-06-15 16:40 +0000 [79bf56c78a]  Corey Farrell <git at cfware.com>
+
+	* func_pjsip_aor: Fix leaked contact from iterator.
+
+	  ASTERISK-25162 #close
+
+	  Change-Id: Id79aa3c6fe490016ee98efc97ac4c1d3f461f97e
+
+2015-06-12 16:58 +0000 [31c77b157b]  Kevin Harwell <kharwell at digium.com>
+
+	* res_pjsip: Add option to force G.726 to be treated as AAL2 packed.
+
+	  Some phones send g.726 audio packed for AAL2, which differs from what is
+	  recommended by RFC 3351. If Asterisk receives audio formatted as such when
+	  negotiating g.726 then it sounds a bit distorted. Added an option to
+	  res_pjsip_endpoint that allows g.726 negotiated audio to be treated as g.726
+	  AAL2 packed.
+
+	  ASTERISK-25158 #close
+	  Reported by: Steve Pitts
+
+	  Change-Id: Ie7e21f75493d7fe53e75e12c971e72f5afa33615
+
+2015-06-14 19:48 +0000 [de8c7f46ed]  Matt Jordan <mjordan at digium.com>
+
+	* main/cdr: Carry over the disable flag when 'disable all' is specified
+
+	  The CDR_PROP function (as well as the NoCDR application) set the
+	  'disable all' flag (AST_CDR_FLAG_DISABLE_ALL) on the current CDR. This
+	  flag is supposed to be applied to all CDRs that are currently in the
+	  chain, as well as all CDRs that may be created in the future. Currently,
+	  however, the flag is only applied to the existing CDRs in the chain; new
+	  CDRs do not receive the 'disable all' flag. In particular, this affects
+	  parallel dials, which generate new CDRs for each pair of channels in
+	  the dial attempt.
+
+	  This patch carries over the 'disable all' flag when it is specified on a
+	  CDR and a new CDR is generated for the chain.
+
+	  ASTERISK-24344 #close
+
+	  Change-Id: I91a0f0031e4d147bdf8a68ecd08304d506fb6a0e
+2015-06-12 14:28 +0000 [78ea356e78]  Matt Jordan <mjordan at digium.com>
+
+	* main/cdr: Copy context/exten on chained CDRs for parallel dials in subroutines
+
+	  When a parallel dial occurs, a new CDR will be created for each dial
+	  attempt that is made. In most circumstances, the act of creating each
+	  CDR in the chain will include a step that updates the Party A snapshot,
+	  which causes the context/extension of the Party A to be copied onto the
+	  CDR object.
+
+	  However, when the Party A is in a subroutine, we explicitly do *not*
+	  copy the context/extension onto the CDR. This prevents the Macro or
+	  GoSub routine name from blowing away the context/extension that the
+	  channel was originally executing in. For the original CDR, this is not a
+	  problem: the original CDR already recorded the last known 'good' state
+	  of the channel just prior to it going into the subroutine. However, for
+	  newly generated CDRs in a chain, there is no context/extension set on
+	  them. Since we are in a subroutine, we will never set the Party A's
+	  context/extension on the CDR, and we end up with a CDR with no
+	  destination recorded on it.
+
+	  This patch updates the creation of a chained CDR such that it copies
+	  over the original CDR's context/extension. This is the last known "good"
+	  state of the CDR, and is a reasonable starting point for the newly
+	  generated CDR. In the case where we are not in a subroutine, subsequent
+	  code will update the location of the CDR from the Party A information;
+	  in the case where we are in a subroutine, the context/extension on the
+	  original CDR is the correct information.
+
+	  ASTERISK-24443 #close
+
+	  Change-Id: I6a3ef0d6e458d3b9b30572feaec70f2964f3bc2a
+
+2015-06-11 08:18 +0000 [3f57f3f8ec]  Damian Ivereigh <damo at launtel.net.au>
+
+	* chan_sip.c: Update dialog fromtag after request with auth
+
+	  If a client sends and INVITE which is 401 rejected, then subsequently
+	  sends a new INVITE with the auth info and uses a different fromtag
+	  from the first INVITE, Asterisk will accept the new INVITE as part of
+	  the original dialog - match_req_to_dialog() specifically ignores the
+	  fromtag. However it does not update the stored dialog with the new
+	  fromtag.
+
+	  This results in Asterisk being unable to match future packets that are
+	  part of this dialog (such as the ACK to the OK or the OK to the BYE),
+	  and the call is dropped.
+
+	  This problem was originally found when using an NEC-i SV8100-GE (NEC SIP
+	  Card).
+
+	  * After a successful match of a packet to the dialog, if the packet is
+	    not a SIP_RESPONSE, authentication is present and the fromtags are
+	    different, the stored fromtag is updated with the one from the recent
+	    INVITE.
+
+	  ASTERISK-25154 #close
+	  Reported by: Damian Ivereigh
+	  Tested by: Damian Ivereigh
+
+	  Change-Id: I5c16cf3b409e5ef9f2b2fe974b6bd2a45a6aa17e
+
+2015-06-11 18:52 +0000 [30a0f2d9ac]  Matt Jordan <mjordan at digium.com>
+
+	* chan_pjsip: Set the context and extension on the channel when created
+
+	  Prior to this patch, chan_pjsip was failing to pass the endpoint's
+	  context and the desired extension to the ast_channel_alloc_* routine.
+	  This caused a new channel snapshot to be issued without a context and
+	  extension, which can cause some reporting issues for users of AMI, CEL,
+	  and other APIs. The channel driver would later set the context and
+	  extension on the channel such that the channel would start in the
+	  correct location in the dialplan, but the information reported in the
+	  initial event would be incorrect.
+
+	  This patch modifies the channel driver such that it now passes the
+	  context and extension directly into the allocation routine. This
+	  provides the information in the new channel snapshot published over
+	  Stasis.
+
+	  ASTERISK-25156 #close
+	  Reported by: cloos
+
+	  Change-Id: Ic6f8542836e596db8f662071d118e8f934fdf25e
+
+2015-06-10 18:28 +0000 [dbb067279e]  Joshua Colp <jcolp at digium.com>
+
+	* bridge: When performing a blonde transfer update connected line information.
+
+	  When performing a blonde transfer the code uses the old masquerade
+	  mechanism to move a channel around. As a result of this certain information,
+	  such as connected line, is moved between the channels involved. Upon
+	  completion of the move a frame is queued which is supposed to update the
+	  connected line information on the channel. This does not occur as the
+	  code considers it a redundant update since the masquerade operation
+	  updated the channel (but did not inform it of the new connected line
+	  information). The code also does not queue a connected line update
+	  to be handled by the thread handling the channel. Without this any
+	  other channel that may be loosely involved does not know it is
+	  talking to a different caller.
+
+	  This change does the following to resolve this:
+
+	  1. The indicated connected line information is cleared upon
+	  completion of the masquerade operation when doing a blonde transfer.
+	  This prevents the connected line update from being considered
+	  redundant.
+
+	  2. A connected line update frame is now queued upon the completion
+	  of the masquerade operation so any other channel loosely involved
+	  knows that there is a different caller.
+
+	  ASTERISK-25157 #close
+	  Reported by: Joshua Colp
+
+	  Change-Id: Ibb8798184a1dab3ecd35299faecc420034adbf20
+
+2015-06-11 14:39 +0000 [a2f4d03c87]  Richard Mudgett <rmudgett at digium.com>
+
+	* app_directory: Fix crash when using the alias option 'a'.
+
+	  The voicemail.conf mailbox key/value pair is defined as:
+	  <mailbox>=[<password>[,<full-name>[,<email>[,<pager>[,<options>]]]]]
+	  Where all fields in the value including the field values are optional.
+
+	  Since the parsing code for the mailbox key/value pair is sloppy, this
+	  patch tightens the parsing for the directory information.
+
+	  * Renamed the 'pos' and 'bufptr' variables to 'name' and 'options'
+	  respectively in search_directory_sub().  Those names make more sense.
+
+	  * Made sure that search_directory_sub() is dealing with the voicemail.conf
+	  mailbox options field if it even exists when looking for the 'hidefromdir'
+	  and 'alias' options.
+
+	  * Fix crash if a voicemail.conf mailbox is just
+	  <mailbox>=<password>,<name> when the 'a' option is used.  If there were no
+	  fields after the name then the 'options' pointer was not checked for NULL.
+
+	  * Fix users.conf alias processing if the 'a' option is used.  The wrong
+	  variable was used.
+
+	  ASTERISK-25087 #close
+	  Reported by: Chet Stevens
+
+	  Change-Id: I86052ea77307beddddba5279824d39dc0d593374
+
+2015-06-09 15:31 +0000 [a2b718f4f6]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip.h: Fix some doxygen comments.
+
+	  Change-Id: I4615771077c3c6a0a7273da6d7b5f77af7e8d976
+
+2015-06-05 13:46 +0000 [32ddf6d86b]  Richard Mudgett <rmudgett at digium.com>
+
+	* taskprocessor.c: Remove extra unref from off-nominal path.
+
+	  Change-Id: Iee3bd8c8a528776056972066698fe735f0f6cf60
+
+2015-04-20 16:00 +0000 [cf98c744d5]  Yousf Ateya <y.ateya at starkbits.com>
+
+	* chan_iax2: Prevent deadlock between hangup and sending lagrq/ping
+
+	  channels/chan_iax.c: Prevent the deadlock between iax2_hangup and send_lagrq/
+	  send_ping. This deadlock happens because the scheduled task send_lagrq(or
+	  send_ping) starts execution after the call hangup procedure starts but before
+	  it deletes the tasks in the scheduler.
+
+	  The solution is to delete scheduled lagrq (and ping) task asynchronously
+	  (i.e. schedule AST_SCHED_DEL for these tasks); By this, AST_SCHED_DEL will
+	  be called in a new context (doesn't have callno locked).
+
+	  This commit also cleans up the procedure of sending LAGRQ and PING.
+
+	  main/sched.c: Do not assert when deleting non existant entry from scheduler.
+	  This assert seems to be the reason for a lot of awkward code to avoid it.
+
+	  ASTERISK-24983 #close
+	  Reported by: Y Ateya
+
+	  Change-Id: I03bec1fc8faacb89630269e935fa667c6d6c080c
+
+2015-05-31 12:37 +0000 [8af6c9cf6b]  Ivan Poddubny <ivan.poddubny at gmail.com>
+
+	* res_pjsip_transport_websocket: Fix use-after-free bugs.
+
+	  This patch fixes use-after-free bugs caught by AddressSanitizer.
+
+	  1. PJSIP transport manager may decide to destroy transport on its own.
+	  For example, when the contact registered via websocket has not renewed
+	  its registration in time. The transport was destoyed, but the websocket
+	  listener thread was still active until the socket closes, and then tried
+	  to call transport_shutdown on transport that has been freed.
+
+	  Also, the transport destructor accessed wstransport->rdata.tp_info.pool
+	  right after freeing memory that contained wstransport itself.
+
+	  This patch converts transport to an ao2 object, allowing it to be
+	  refcounted, so that it is available until both websocket listener and
+	  pjsip transport manager are finished with it.
+
+	  2. The websocket listener deletes the last reference on websocket session
+	  when the tcp connection is closed, and it gets destroyed, but
+	  the transport manager may still use it, for example when disconnect
+	  happens in the middle of a SIP transaction.
+
+	  A new reference to websocket session has been added that is released
+	  with the transport to prevent this.
+
+	  ASTERISK-25096 #close
+	  Reported by: Josh Kitchens
+
+	  ASTERISK-24963 #close
+	  Reported by: Badalian Vyacheslav
+
+	  Change-Id: Idc0b63eb6e459c1ddfb2430127d34b3c4d8d373b
+
+2015-06-09 13:41 +0000 [3046bc17ed]  ibercom <ibercom123 at gmail.com>
+
+	* weakref attribute detection broken with gcc 4.6 and higher
+
+	  GCC 4.7 Manual:
+	  http://gcc.gnu.org/onlinedocs/gcc-4.7.4/gcc/Function-Attributes.html
+
+	  weakref ("target")
+
+	  A weak reference is an alias that does not by itself require a definition
+	  to be given for the target symbol.
+
+	  ASTERISK-22559 #close
+	  Reported by: Ibercom
+
+	  Change-Id: I36a136cae947b65187a697533416f9ff9a0b8cdf
+
+2015-06-08 10:09 +0000 [55c8daf88b]  Corey Farrell <git at cfware.com>
+
+	* Fix unsafe uses of ast_context pointers.
+
+	  Although ast_context_find, ast_context_find_or_create and
+	  ast_context_destroy perform locking of the contexts table,
+	  any context pointer can become invalid at any time that the
+	  contexts table is unlocked. This change adds locking around
+	  all complete operations involving these functions.
+
+	  Places where ast_context_find was followed by ast_context_destroy
+	  have been replaced with calls ast_context_destroy_by_name.
+
+	  ASTERISK-25094 #close
+	  Reported by: Corey Farrell
+
+	  Change-Id: I1866b6787730c9c4f3f836b6133ffe9c820734fa
+
+2015-06-04 07:14 +0000 [e0090216db]  ibercom <ibercom123 at gmail.com>
+
+	* CLI: Cosmetic issue - core show uptime
+
+	  Show uptime information ends with an unnecessary space.
+
+	  Now NEEDCOMMA is better defined.
+
+	  Change-Id: I11b360504a0703309ff51772ff8f672287f3c5a1
+
+2015-06-03 17:41 +0000 [88212ccb7f]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip: Prevent access of NULL channels.
+
+	  It is possible to receive incoming requests or responses after the channel
+	  on an ast_sip_session has been destroyed and NULLed out. Handlers of these
+	  sorts of requests or responses need to be prepared for the possibility
+	  that the channel is NULL or else they could cause a crash.
+
+	  While several places have been amended to deal with NULL channels, there
+	  were still a couple of places that needed updating.
+
+	  res_pjsip_dtmf_info.c: When handling incoming INFO requests, we need to
+	  return early if there is no channel on the session.
+
+	  res_pjsip_session.c: When handling a 302 response, we need to stop the
+	  redirecting attempt if there is no channel on the session.
+
+	  ASTERISK-25148 #close
+	  reported by Mark Michelson
+
+	  Change-Id: Id1a75ffc3d0eaa168b0b28188fb54d6cf9fc47a9
+
+2015-06-01 11:45 +0000 [f5d5aa67dc]  Kevin Harwell <kharwell at digium.com>
+
+	* AMI: Escape string values.
+
+	  So this issue is a bit complicated. Since it is possible to pass values to AMI
+	  that contain a '\r\n' (or other similar sequences) these values need to be
+	  escaped. One way to solve this is to escape the values and then pass the escaped
+	  values to the AMI variable parameter string building function. However, this
+	  puts the onus on the pre-build function to escape all string values. This
+	  potentially requires a fair amount of changes along with a lot of string
+	  allocations/freeing for all values.
+
+	  Surely there is a way to push this complexity down a level into the string
+	  building function itself? This of course is possible, but ends up requiring a
+	  way to distinguish between strings that need to be escaped and those that don't.
+	  The best way to handle this is by introducing a new format specifier in the
+	  format string. For instance a %s (no escape) and %S (escape). However, that is
+	  a bit weird and unexpected.
+
+	  So faced with those possibilities this patch implements a limited version of the
+	  first option. Instead of attempting to escape all string values this patch only
+	  escapes those values that make sense. This approach limits the number of changes
+	  and doesn't suffer from the odd format specifier problem.
+
+	  ASTERISK-24934 #close
+	  Reported by: warren smith
+
+	  Change-Id: Ib55a5b84fe0481b0f2caaaab68c566f392c0aac0
+
+2015-06-03 13:17 +0000 [5dc9fb4198]  gtjoseph <george.joseph at fairview5.com>
+
+	* res_pjsip/location: Fix ref leak in contact_apply_handler
+
+	  contact_apply_handler calls ast_res_pjsip_find_or_create_contact_status
+	  to force the creation of a contact_status object whenever a new
+	  contact is added but it didn't unref the returned object.
+
+	  Added an ao2_cleanup(status) to plug the leak.
+
+	  ASTERISK-25141
+
+	  Change-Id: Icc1401cae142855a1abc86ab5179dfb3ee861c40
+	  Reported-by: Corey Farrell
+
+2015-06-02 15:07 +0000 [d908272b7e]  David M. Lee <dlee at respoke.io>
+
+	* Fixes for OS X
+
+	   * Add some type casting so tv_usec can really be a long, instead of
+	     some strange platform specific type.
+
+	   * Add some .dylib style files to .gitignore.
+
+	   * Switch from using -Xlinker to -Wl,. For [reasons unknown][], newer
+	     versions of GCC, when compiling the Homebrew formula for Asterisk,
+	     are not properly passing the -Xlinker options to the linker. Given
+	     that -Wl, does exactly the [same thing][], and does it properly, this
+	     patch changes the -Xlinker options to use -Wl, instead.
+
+	   [reasons unknown]: http://bit.ly/1SUbEYx
+	   [same thing]: https://gcc.gnu.org/onlinedocs/gcc/Link-Options.html
+
+	  Change-Id: Id5e6b3c6cc86282ea5fca630dc3991137c5bf4dd
+
+2015-05-30 20:22 +0000 [9e7827e3ac]  Corey Farrell <git at cfware.com>
+
+	* pjsip_configuration: Fix leak in persistent_endpoint_update_state.
+
+	  The loop to find the first available contact of an endpoint grabbed
+	  contact from the iterator, then checked for offline state.  This
+	  caused the first contact after the state was found to leak a reference.
+
+	  ASTERISK-25141
+
+	  Change-Id: Id0f1d87410fc63742db0594eb4b18b36e99aec08
+2015-05-31 11:33 +0000 [888bb49618]  Ivan Poddubny <ivan.poddubny at gmail.com>
+
+	* Fix buffer overflow in slin sample frames generation.
+
+	  The length of frames retured by sample functions was twice as large as
+	  real, what caused global buffer overflow caught by AddressSanitizer.
+
+	  ASTERISK-24717 #close
+	  Reported by: Badalian Vyacheslav
+
+	  Change-Id: Iec2fe682aef13e556684912f906bedf7c18229c6
+
+2015-05-29 16:19 +0000 [857166b5e5]  gtjoseph <george.joseph at fairview5.com>
+
+	* res_pjsip/location:  Fix memory leak in permanent_uri_handler
+
+	  When permanent_uri_handler was creating the contact status
+	  object for each contact, it wasn't unreffing it at the
+	  end of the loop.
+
+	  ASTERISK-25141 #close
+	  Reported-by: Corey Farrell
+
+	  Change-Id: I7bb127994677bb3d459f87952f8425c9b9967b12
+
+2015-05-29 14:52 +0000 [1558a89129]  gtjoseph <george.joseph at fairview5.com>
+
+	* Revert "endpoint/stasis: Eliminate duplicate events on endpoint status change"
+
+	  This reverts commit 35c699086ae2fd81b2473307ccb2ae79ad32375a.
+
+	  Change-Id: Ia98c2b4820cf579a5b9bb75e9e05d7a233205fb7
+
+2015-05-27 13:22 +0000 [35c699086a]  gtjoseph <george.joseph at fairview5.com>
+
+	* endpoint/stasis: Eliminate duplicate events on endpoint status change
+
+	  When an endpoint was created, it's messages were being forwarded to
+	  both the tech endpoint topic and the all endpoints topic.  Since
+	  the tech topic was also forwarded to all, this was resulting in
+	  duplicate messages whenever an endpoint published.  This patch
+	  causes the endpoint to only forward to the tech topic and lets
+	  the tech topic forward to all.
+
+	  To accomplish this, the existing stasis_cp_single_create function
+	  (which both creates and forwards) was cloned and split into 2
+	  functions, one that creates the topic and one that sets up the
+	  forwarding.  This allows endpoint_internal_create to create
+	  the topic from the endpoint_all cache without forwarding it there,
+	  then allows it to do the forward to the tech's topic.
+
+	  ASTERISK-25137 #close
+	  Reported-by: Vitezslav Novy
+	  ASTERISK-25116 #close
+	  Reported-by: George Joseph <george.joseph at fairview5.com>
+	  Tested-by: George Joseph <george.joseph at fairview5.com>
+
+	  Change-Id: I26d7d4926a0861748fd3bdffe316b75b549a801c
+
+2015-05-26 13:56 +0000 [fe21f2e52f]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_session: Fix in-dialog authentication.
+
+	  When the remote peer requires authentication for in-dialog requests then
+	  re-INVITEs to the peer cause the call to be disconnected and other
+	  in-dialog requests to the peer like MESSAGE just don't go through.
+
+	  * Made session_inv_on_tsx_state_changed() handle in-dialog authentication
+	  for re-INVITEs and other methods.  Initial INVITEs cannot be handled here
+	  because the INVITE transaction must be restarted earlier.
+
+	  * Pulled needed code from res/res_pjsip/pjsip_outbound_auth.c in
+	  preparation for removing the file.  The generic outbound authentication
+	  code did not work as well as anticipated.
+
+	  * Created outbound_invite_auth() to only handle initial outbound INVITEs.
+	  Re-INVITEs cannot be handled here.  The re-INVITE transaction is still in
+	  progress and the PJSIP library cannot handle the overlapping INVITE
+	  transactions.  Other method types should not be handled here as this code
+	  only works on outgoing calls and we need to handle incoming and outgoing
+	  calls.
+
+	  ASTERISK-25131 #close
+	  Reported by: Richard Mudgett
+
+	  Change-Id: I12bdd7ddccc819b4ce4b091e826d1e26334601b0
+
+2015-05-21 17:21 +0000 [262d590819]  gtjoseph <george.joseph at fairview5.com>
+
+	* res_pjsip: Add AMI events for chan_pjsip contact lifecycle changes
+
+	  Add a new ContactStatus AMI event.
+	  Publish the following status/state changes:
+	  Created
+	  Removed
+	  Reachable
+	  Unreachable
+	  Unknown
+
+	  Contact URI, new status/state, aor and endpoint names, and the
+	  last qualify rtt result are included in the event.
+
+	  ASTERISK-25114 #close
+
+	  Change-Id: Id25aae5f7122facba183273efb3e8f36c20fb61e
+	  Reported-by: George Joseph <george.joseph at fairview5.com>
+	  Tested-by: George Joseph <george.joseph at fairview5.com>
+
+2015-05-26 07:44 +0000 [5a42397018]  Joshua Colp <jcolp at digium.com>
+
+	* sorcery: Fix cache creation callback.
+
+	  The cache creation callback function expects to receive a sorcery_details
+	  structure and not just a standalone object.
+
+	  Change-Id: I3e4a5a137cb25292eb52d7a14cbb6daa09213450
+
+2015-05-24 13:47 +0000 [97a6ce1717]  Ivan Poddubny <ivan.poddubny at gmail.com>
+
+	* Astobj2: Correctly treat hash_fn returning INT_MIN
+
+	  The code in astobj2_hash.c wrongly assumed that abs(int) is always > 0.
+	  However, abs(INT_MIN) = INT_MIN and is still negative, as well as
+	  abs(INT_MIN) % num_buckets, and as a result this led to a crash.
+
+	  One way to trigger the bug is using host=::80 or 0.0.0.128 in peer
+	  configuration section in chan_sip or chan_iax.
+
+	  This patch takes the remainder before applying abs, so that bucket
+	  number is always in range.
+
+	  ASTERISK-25100 #close
+	  Reported by: Mark Petersen
+
+	  Change-Id: Id6981400ad526f47e10bcf7b847b62bd2785e899
+2015-05-23 04:36 +0000 [554bd1e39c]  Ivan Poddubny <ivan.poddubny at gmail.com>
+
+	* res_pjsip_transport_websocket: Fix crash on receiving large SIP packets
+
+	  Incoming SIP packets larger than PJSIP_MAX_PKT_LEN were themselves
+	  truncated before passing to pjsip_tpmgr_receive_packet, but the length
+	  was passed unaltered, thus causing memory corruption and segfault.
+
+	  ASTERISK-25122 #close
+
+	  Change-Id: I608a6b6b7f229eacc33a0a7d771d18e27e5b08ab
+
+2015-05-22 21:50 +0000 [0d266cbe02]  Corey Farrell <git at cfware.com>
+
+	* Stasis: Fix unsafe use of stasis_unsubscribe in modules.
+
+	  Many uses of stasis_unsubscribe in modules can be reached through unload.
+	  These have been switched to stasis_unsubscribe_and_join.
+
+	  Some subscription callbacks do nothing, for these I've created a noop
+	  callback function in stasis.c.  This is used by some modules that monitor
+	  MWI topics in order to enable cache, since the callback does not become
+	  invalid after dlclose it is safe to use stasis_unsubscribe on these, even
+	  during module unload.
+
+	  ASTERISK-25121 #close
+
+	  Change-Id: Ifc2549fbd8eef7d703c222978e8f452e2972189c
+
+2015-05-22 12:22 +0000 [51ffed5e61]  Matt Jordan <mjordan at digium.com>
+
+	* res/res_pjsip_pubsub: Note that 'dialog' is also a valid event type for RLS
+
+	  In addition to specifying lists of 'presence' and 'message-summary',
+	  users can also create lists of type 'dialog'. These should be treated in
+	  the same fashion as 'presence'.
+
+	  Change-Id: I583bb69cd9f88b0b29bf09ddaddeac4e84189f6e
+
+2015-05-22 12:18 +0000 [7950b65e4f]  Matt Jordan <mjordan at digium.com>
+
+	* res/res_pjsip_exten_state: Fix confusing NOTICE message
+
+	  When a SUBSCRIBE request is made to a dialplan hint that doesn't exist,
+	  the current NOTICE message informing users of this swaps the context and
+	  extension parameters. This can cause a bit of confusion.
+
+	  Thanks to CptBurger in #asterisk for helping to point this out.
+
+	  Change-Id: Ie584d1a58ae217385c87a450ca25b55ca0e36e43
+
+2015-05-17 20:36 +0000 [5ac65ddfb4]  Matt Jordan <mjordan at digium.com>
+
+	* res/ari: Register Stasis application on WebSocket attempt
+
+	  Prior to this patch, when a WebSocket connection is made, ARI would not
+	  be informed of the connection until after the WebSocket layer had
+	  accepted the connection. This created a brief race condition where the
+	  ARI client would be notified that it was connected, a channel would be
+	  sent into the Stasis dialplan application, but ARI would not yet have
+	  registered the Stasis application presented in the HTTP request that
+	  established the WebSocket.
+
+	  This patch resolves this issue by doing the following:
+	   * When a WebSocket attempt is made, a callback is made into the ARI
+	     application layer, which verifies and registers the apps presented in
+	     the HTTP request. Because we do not yet have a WebSocket, we cannot
+	     have an event session for the corresponding applications. Some
+	     defensive checks were thus added to make the application objects
+	     tolerant to a NULL event session.
+	   * When a WebSocket connection is made, the registered application is
+	     updated with the newly created event session that wraps the WebSocket
+	     connection.
+
+	  ASTERISK-24988 #close
+	  Reported by: Joshua Colp
+
+	  Change-Id: Ia5dc60dc2b6bee76cd5aff0f69dd53b36e83f636
+
+2015-05-20 11:11 +0000 [60e2fbfe62]  gtjoseph <george.joseph at fairview5.com>
+
+	* res_pjsip: Refactor endpt_send_transaction (qualify_timeout)
+
+	  This patch refactors the transaction timeout processing to eliminate
+	  calling the lower level public pjsip functions and reverts to calling
+	  pjsip_endpt_send_request again.  This is the result of me noticing
+	  a possible incompatibility with pjproject-2.4 which was causing
+	  contact status flapping.
+
+	  The original version of this feature used the lower level calls to
+	  get access to the tsx structure in order to cancel the transaction
+	  when our own timer expires. Since we no longer have that access,
+	  if our own timer expires before the pjsip timer, we call the callbacks
+	  and just let the pjsip transaction take it's own course.  When the
+	  transaction ends, it discovers the callbacks have already been run
+	  and just cleans itself up.
+
+	  A few messages in pjsip_configuration were also added/cleaned up.
+
+	  ASTERISK-25105 #close
+
+	  Change-Id: I0810f3999cf63f3a72607bbecac36af0a957f33e
+	  Reported-by: George Joseph <george.joseph at fairview5.com>
+	  Tested-by: George Joseph <george.joseph at fairview5.com>
+2015-05-20 00:45 +0000 [42476e6633]  demon-ru <serov.d.p at gmail.com>
+
+	* res_pjsip_outbound_registration: Check request URI for line.
+
+	  When an inbound call is received the To header is checked
+	  for the "line" option. Some remote servers will place this
+	  in the request URI instead. This adds an additional check for
+	  the option in the request URI.
+
+	  ASTERISK-25072 #close
+	  Reported by: Dmitriy Serov
+
+	  Change-Id: Id4e44debbb80baad623b914a88574371575353c8
+
+2015-05-21 17:51 +0000 [e7edb59db6]  Corey Farrell <git at cfware.com>
+
+	* res_mwi_external_ami: Use module version of AMI registration.
+
+	  Use ast_manager_register_xml for res_mwi_external_ami manager
+	  actions.  This ensures the module is held open while any of
+	  the actions are being run.
+
+	  ASTERISK-25117 #close
+	  Reported by: Corey Farrell
+
+	  Change-Id: Iececfdc2da498b2c32b9e09042f5f12292007ac7
+2015-05-21 19:59 +0000  Asterisk Development Team <asteriskteam at digium.com>
+
+	* asterisk 13.4.0-rc1 Released.
+
+2015-05-21 14:56 +0000 [3fb2b375fe]  Matt Jordan <mjordan at digium.com>
+
+	* Release summaries: Remove previous versions
+
+2015-05-21 14:56 +0000 [9d9ae03842]  Matt Jordan <mjordan at digium.com>
+
+	* .version: Update for 13.4.0-rc1
+
+2015-05-21 14:56 +0000 [53a39083e5]  Matt Jordan <mjordan at digium.com>
+
+	* .lastclean: Update for 13.4.0-rc1
+
+2015-05-21 14:56 +0000 [7af8ef9346]  Matt Jordan <mjordan at digium.com>
+
+	* realtime: Add database scripts for 13.4.0-rc1
+
+2015-05-21 14:52 +0000 [20982c68d4]  Matt Jordan <mjordan at digium.com>
+
+	* Release summaries: Correct summaries for 13.4.0-rc1
+
+2015-05-21 13:17 +0000 [1bb62b037f]  mjordan <mjordan at lunkwill>
+
+	* ChangeLog: Updated for 13.4.0-rc1
+
+2015-05-21 13:17 +0000 [1e98a36699]  mjordan <mjordan at lunkwill>
+
+	* Release summaries: Add summaries for 13.4.0-rc1
+
+2015-05-21 13:15 +0000 [5c12e5ba72]  mjordan <mjordan at lunkwill>
+
+	* .version: Update for 13.4.0-rc1
+
+2015-05-21 13:15 +0000 [69292a9f11]  mjordan <mjordan at lunkwill>
+
+	* .lastclean: Update for 13.4.0-rc1
+
+2015-05-21 13:15 +0000 [628680803a]  mjordan <mjordan at lunkwill>
+
+	* realtime: Add database scripts for 13.4.0-rc1
+
+2015-05-21 13:05 +0000 [9d8a462356]  Matt Jordan <mjordan at digium.com>
+
+	* ARI: Update version to 1.7.0
+
+	  This patch updates the version of ARI to 1.7.0 to reflect the backwards
+	  compatible changes that will be introduced in 13.4.0.
+
+	  Change-Id: I6c36e6144da426412f25828a868e4df916bff60a
+
+2015-05-21 07:22 +0000 [620054c527]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "audiohook.c: Difference in read/write rates caused continuous buffer resets" into 13
+2015-05-21 07:21 +0000 [f5e195b44e]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "Logger: Reset defaults before processing config." into 13
+2015-05-21 07:20 +0000 [e8a4e01c32]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "res/res_http_websocket: Add a pre-session established callback" into 13
+2015-05-21 05:15 +0000 [3c98544543]  Joshua Colp <jcolp at digium.com>
+
+	* Merge "main/sdp_srtp.c: allow SDP crypto tag to be up to 9 digits" into 13
+2015-05-20 20:53 +0000 [9b6e228419]  Corey Farrell <git at cfware.com>
+
+	* Logger: Reset defaults before processing config.
+
+	  Reset options to default values before reloading config.  This ensures
+	  that if a setting is removed or commented out of the configuration file
+	  it is unset on reload.
+
+	  ASTERISK-25112 #close
+	  Reported by: Corey Farrell
+
+	  Change-Id: Id24bb1fb0885c2c14cf8bd6f69a0c2ee7cd6c5bd
+
+2015-05-20 19:05 +0000 [7fcf0a97b8]  George Joseph <george.joseph at fairview5.com>
+
+	* app_playback: Suppress warnings on playback if channel hung up
+
+	  If a channel hangs up while an audio file is playing, there's
+	  no need to clutter up the logs with a warning so suppress it
+	  if ast_check_hangup returns true.
+
+	  Also, change warning to debug/2 in file.c if writing a frame
+	  fails.  Same reasoning.
+
+	  Change-Id: I2e66191af3c5b6e951c98e8f1c3fe3cf2cf7ed89
+	  Reported-by: George Joseph <george.joseph at fairview5.com>
+	  Tested-by: George Joseph <george.joseph at fairview5.com>
+
+2015-05-14 15:21 +0000 [b1e8c0b9eb]  Kevin Harwell <kharwell at digium.com>
+
+	* audiohook.c: Difference in read/write rates caused continuous buffer resets
+
+	  Currently, everytime a sample rate change occurs (on read or write) the
+	  associated factory buffers are reset. If the requested sample rate on a
+	  read differed from that of a write then the buffers are continually reset
+	  on every read and write. This has the side effect of emptying the buffer,
+	  thus there being no data to read and then write to a file in the case of
+	  call recording.
+
+	  This patch fixes it so that an audiohook_list's rate always maintains the
+	  maximum sample rate among hooks and formats. Audiohook sample rates are
+	  only overwritten by this value when slin native compatibility is turned on.
+	  Also, the audiohook sample rate can only overwrite the list's sample rate
+	  when its rate is greater than that of the list or if compatibility is
+	  turned off. This keeps the rate from constantly switching/resetting.
+
+	  ASTERISK-24944 #close
+	  Reported by: Ronald Raikes
+
+	  Change-Id: Idab4dfef068a7922c09cc631dda27bc920a6c76f
+
+2015-05-20 15:22 +0000 [4a450f863b]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "Fix potential crash after unload of func_periodic_hook or test_message." into 13
+2015-05-19 13:01 +0000 [17d6ede337]  Corey Edwards <tensai at zmonkey.org>
+
+	* main/sdp_srtp.c: allow SDP crypto tag to be up to 9 digits
+
+	  ASTERISK-24887 #close
+	  Reported by: Makoto Dei
+	  Tested by: tensai
+
+	  Change-Id: I6a96f572adb17f76b3acafe503a01c48eb5dd9bf
+
+2015-05-13 09:55 +0000 [31cc24aad6]  Matt Jordan <mjordan at digium.com>
+
+	* res/res_http_websocket: Add a pre-session established callback
+
+	  This patch updates http_websocket and its corresponding implementation
+	  with a pre-session established callback. This callback allows for
+	  WebSocket server consumers to be notified when a WebSocket connection is
+	  attempted, but before we accept it. Consumers can choose to reject the
+	  connection, if their application specific logic allows for it.
+
+	  As a result, this patch pulls out the previously private
+	  websocket_protocol struct and makes it public, as
+	  ast_websocket_protocol. In order to preserve backwards compatibility
+	  with existing modules, the existing APIs were left as-is, and new APIs
+	  were added for the creation of the ast_websocket_protocol as well as for
+	  adding a sub-protocol to a WebSocket server.
+
+	  In particular, the following new API calls were added:
+	  * ast_websocket_add_protocol2 - add a protocol to the core WebSocket
+	    server
+	  * ast_websocket_server_add_protocol2 - add a protocol to a specific
+	    WebSocket server
+	  * ast_websocket_sub_protocol_alloc - allocate a sub-protocol object.
+	    Consumers can populate this with whatever callbacks they wish to
+	    support, then add it to the core server or a specified server.
+
+	  ASTERISK-24988
+	  Reported by: Joshua Colp
+
+	  Change-Id: Ibe0bbb30c17eec6b578071bdbd197c911b620ab2
+
+2015-05-14 22:05 +0000 [f9114179e6]  snuffy <snuffy22 at gmail.com>
+
+	* chan_pjsip: Fix crash during off-nominal when no endpoint specified.
+
+	  Add missing return -1 when no endpoint name is specified.
+
+	  ASTERISK-25086 #close
+	  Reported by: snuffy
+
+	  Change-Id: I9de76c2935a1f4e3f0cffe97a670106f5605e89e
+2015-05-14 18:01 +0000 [dd78ab42e4]  George Joseph <george.joseph at fairview5.com>
+
+	* res_pjsip_config_wizard/config: Fix template processing
+
+	  The config wizard was always pulling the first occurrence of
+	  a variable from an ast_variable list but this gets the template
+	  value from the list instead of any overridden value.  This patch
+	  creates ast_variable_find_last_in_list() in config.c and updates
+	  res_pjsip_config_wizard to use it instead of
+	  ast_variable_find_in_list.  Now the overridden values, where they
+	  exist, are used instead of template variables.
+
+	  Updated test_config to test the new API.
+
+	  ASTERISK-25089 #close
+
+	  Reported-by: George Joseph <george.joseph at fairview5.com>
+	  Tested-by: George Joseph <george.joseph at fairview5.com>
+	  Change-Id: Ifa7ddefc956a463923ee6839dd1ebe021c299de4
+
+2015-05-15 01:54 +0000 [091b436007]  snuffy <snuffy22 at gmail.com>
+
+	* cdr: Fix 'core show channel' CDR variable truncation.
+
+	  When the new Bridging API was implemented, the workspace variable
+	  changed to a malloc'd string, causing sizeof() to always be 8 (char).
+
+	  Revert back to stored on stack string for workspace.
+
+	  ASTERISK-25090 #close
+
+	  Change-Id: I51e610ae87371df771ce7693a955510efb90f8f7
+2015-05-14 15:20 +0000 [8697a49ef9]  Joshua Colp <jcolp at digium.com>
+
+	* Merge "sorcery: Add API to insert/remove a wizard to/from an object type's list" into 13
+2015-05-14 15:19 +0000 [aea349a87e]  Joshua Colp <jcolp at digium.com>
+
+	* Merge "Message.c: Clear message channel frames on cleanup" into 13
+2015-05-14 00:06 +0000 [6b7282ca40]  Corey Farrell <git at cfware.com>
+
+	* Fix potential crash after unload of func_periodic_hook or test_message.
+
+	  These modules save a pointer to the context they create on load, and
+	  use that pointer to destroy the context at unload.  It is not safe
+	  to save this pointer, it is replaced during load of pbx_config,
+	  pbx_lua or pbx_ael.
+
+	  This change causes the modules to pass NULL to ast_context_destroy,
+	  a safer way to perform the unregistration since it does not use
+	  a pointer that could become invalid.
+
+	  ASTERISK-25085 #close
+	  Reported by: Corey Farrell
+
+	  Change-Id: I6a00ec8e38046058f97dc703e1adcde9bf517835
+2015-05-14 05:02 +0000 [8f8d54a18e]  Joshua Colp <jcolp at digium.com>
+
+	* Merge "main/manager.c: Bugfix sort action_manager by alphabetically" into 13
+2015-05-13 15:41 +0000 [02c5130589]  Jonathan Rose <jrose at digium.com>
+
+	* Message.c: Clear message channel frames on cleanup
+
+	  The message channel is a special channel that doesn't actually process frames.
+	  However, certain actions can cause frames to be placed in the channel's read
+	  queue including the Hangup application which is called on the channel after
+	  each message is processed. Since the channel will continually be reused for
+	  many messages, it's necessary to flush these frames at some point.
+
+	  ASTERISK-25083 #close
+	  Reported by: Jonathan Rose
+
+	  Change-Id: Idf18df73ccd8c220be38743335b5c79c2a4c0d0f
+
+2015-05-13 15:44 +0000 [586da882bc]  Joshua Colp <jcolp at digium.com>
+
+	* Merge "app_voicemail: fix moving when old messages full" into 13
+2015-05-12 17:45 +0000 [d49d64b79c]  Jonathan Rose <jrose at digium.com>
+
+	* app_voicemail: fix moving when old messages full
+
+	  When completing voicemail playback of a message in the 'INBOX', the
+	  message gets moved to the 'Old' messages folder. Without this patch, if
+	  the 'Old' folder is already at its set limit, then the 'INBOX' message will
+	  simply be deleted. With this patch, the flag to delete the message will be
+	  removed if the save_to_folder function indicates that the message could
+	  not be moved due to a full folder.
+
+	  ASTERISK-25082 #close
+	  Reported by: Jonathan Rose
+	  Review: https://gerrit.asterisk.org/#/c/448/
+
+	  Change-Id: I2be440a09f42e2d06d50975c40d1ad7f836ecb3f
+2015-05-13 14:20 +0000 [51478575e4]  Joshua Colp <jcolp at digium.com>
+
+	* Merge "General: Fix recent menuselect-related cross compile regression" into 13
+2015-05-13 12:26 +0000 [5fcaf727cc]  Joshua Colp <jcolp at digium.com>
+
+	* Merge "res_config_mysql: Fix broken column type checking" into 13
+2015-05-13 12:24 +0000 [6a12b0634b]  Joshua Colp <jcolp at digium.com>
+
+	* Merge "chan_dahdi/sig_pri: Fix crash on ISDN call hangup collision." into 13
+2015-05-04 20:11 +0000 [9b13536fed]  Rodrigo Ramírez Norambuena <decipher.hk at gmail.com>
+
+	* main/manager.c: Bugfix sort action_manager by alphabetically
+
+	  Fix the alphabetic order added on ast_manager_register_struct. The order
+	  for struct manager_action added is not working, this change fixes the
+	  problem.
+
+	  Change-Id: I149da0cd06c3c4445d7516cc303358e9f26f8b4b
+
+2015-05-08 18:01 +0000 [e67e8d5c7f]  Alexandre Fournier <alexandre.fournier at kiplink.fr>
+
+	* res_config_mysql: Fix broken column type checking
+
+	  MySQL configuration engine contains a bug in require_mysql(). This
+	  function is used for column type checking in tables. This bug only
+	  affects DATETIME, DATE and FLOAT types.
+
+	  It came from mixing the first condition (switch-case-like
+	  if/then/else), to check the expected column type, with the second
+	  condition, to check the actual column type against the expected column
+	  type. Both conditions must be checked separately in order to avoid the
+	  execution of the wrong block.
+
+	  ASTERISK-18252 #comment This patch might fix the issue
+	  Reported by: Gareth Blades
+
+	  ASTERISK-25041 #close
+	  Reported by: Alexandre Fournier
+	  Tested by: Alexandre Fournier
+
+	  Change-Id: I0b8bf7e68ab938be8e6525a249260cb648cb0bfa
+
+2015-05-10 07:37 +0000 [16f602f5c2]  Yousf Ateya <y.ateya at starkbits.com>
+
+	* res_rtp_asterisk: Correction for the limit which detects that a packet is DTLS.
+
+	  First byte of DTLS packet shall be in range 20-63, not 20-64. Refer to RFC
+	  https://tools.ietf.org/html/rfc5764#section-5.1.2 for correct values.
+
+	  Change-Id: Iae6fa0d72b37c36a27fe40686e0ae6fba3afec31
+
+2015-05-13 04:35 +0000 [62422712f7]  Joshua Colp <jcolp at digium.com>
+
+	* Merge "cdr_pgsql: Use PQescapeStringConn for escaping names." into 13
+2015-05-12 17:34 +0000 [c780b6e431]  Richard Mudgett <rmudgett at digium.com>
+
+	* chan_dahdi/sig_pri: Fix crash on ISDN call hangup collision.
+
+	  If an ISDN call is hungup by both sides at the same time a crash could
+	  happen.
+
+	  * Added missing NULL checks for the owner channel after calling
+	  pri_queue_pvt_cause_data() in two places.  Code after those calls need to
+	  check the owner channel pointer for NULL before use because
+	  pri_queue_pvt_cause_data() needs to do deadlock avoidance to lock the
+	  owner and the owner may get hung up.
+
+	  ASTERISK-21893 #close
+	  Reported by:  Alexandr Gordeev
+
+	  Change-Id: Ica3e266ebc7a894b41d762326f08653e1904bb9a
+
+2015-05-10 02:26 +0000 [6627de830b]  Sebastian Kemper <sebastian_ml at gmx.net>
+
+	* General: Fix recent menuselect-related cross compile regression
+
+	  MAKE_MENUSELECT currently sets CC to CC, which is the compiler for the
+	  target platform. But menuselect is to be run on the build system, so
+	  BUILD_CC needs to be used instead - like it was in the past, before the
+	  recent changes (https://reviewboard.asterisk.org/r/4370/). This is the
+	  patch for ASTERISK-25074.
+
+	  ASTERISK-25074 #close
+	  Reported by: Sebastian Kemper
+	  Tested by: Sebastian Kemper
+
+	  Change-Id: I8a2b1fc5deb6ad2b80f49baca35b1b13d468ebf8
+
+2015-05-05 15:32 +0000 [637c8f065e]  George Joseph <george.joseph at fairview5.com>
+
+	* sorcery: Add API to insert/remove a wizard to/from an object type's list
+
+	  Currently you can 'apply' a wizard to an object type but the wizard
+	  always goes at the end of the object type's wizard list.  This patch
+	  adds a new ast_sorcery_insert_wizard_mapping function that allows
+	  you to insert a wizard anyplace in the list.  I.E.  You could
+	  add a caching wizard to an object type and place it before all
+	  wizards.
+
+	  ast_sorcery_get_wizard_mapping_count and
+	  ast_sorcery_get_wizard_mapping were added to allow examination
+	  of the mapping list.
+
+	  ast_sorcery_remove_mapping was added to remove a mapping by name.
+
+	  As part of this patch, the object type's wizard list was converted
+	  from an ao2_container to an AST_VECTOR_RW.
+
+	  A new test was added to test_sorcery for this capability.
+
+	  ASTERISK-25044 #close
+
+	  Change-Id: I9d2469a9296b2698082c0989e25e6848dc403b57
+
+2015-05-12 01:31 +0000 [3cdb7950f0]  Corey Farrell <git at cfware.com>
+
+	* Fix processing of asterisk.conf debug=yes.
+
+	  The code which reads asterisk.conf supports processing the debug
+	  option with ast_true, but ast_true returns -1.  This causes debug
+	  to still be off, convert to 1 so debug will be on as requested.
+
+	  ASTERISK-25042
+	  Reported by: Corey Farrell
+
+	  Change-Id: I3c898b7d082d914b057e111b9357fde46bad9ed6
+
+2015-05-01 23:43 +0000 [6553a00770]  Rodrigo Ramírez Norambuena <decipher.hk at gmail.com>
+
+	* cdr_pgsql: Use PQescapeStringConn for escaping names.
+
+	  Use function PQescapeStringConn for escaping the name
+	  of the table and schema instead of doing it manually.
+
+	  Change-Id: I6709165e2d00463e9c813d24f17830ad4910b599
+
+2015-05-12 05:38 +0000 [8523a5ed09]  Joshua Colp <jcolp at digium.com>
+
+	* Merge "vector:  Add REMOVE, ADD_SORTED and RESET macros" into 13
+2015-05-09 16:58 +0000 [ea917fefaf]  George Joseph <george.joseph at fairview5.com>
+
+	* vector:  Add REMOVE, ADD_SORTED and RESET macros
+
+	  Based on feedback from Corey Farrell and Y Ateya, a few new
+	  macros have been added...
+
+	  AST_VECTOR_REMOVE which takes a parameter to indicate if
+	  order should be preserved.
+
+	  AST_VECTOR_ADD_SORTED which adds an element to
+	  a sorted vector.
+
+	  AST_VECTOR_RESET which cleans all elements from the vector
+	  leaving the storage intact.
+
+	  Change-Id: I41d32dbdf7137e0557134efeff9f9f1064b58d14
+
+2015-05-11 07:07 +0000 [d5864a358c]  Ivan Poddubny <ivan.poddubny at gmail.com>
+
+	* pbx/pbx_spool: Fix issue when call files were executed too early
+
+	  pbx_spool used to delete/move the call file upon successful outgoing
+	  call completion, but did not delete it from in-memory list of files
+	  (dirlist, used only when compiled with inotify/kqueue support).
+	  That resulted in an extra attempt to process that filename after
+	  retrytime seconds.
+	  Then, if a new file with the same name appears that is scheduled
+	  in future further than the completed one plus its retrytime,
+	  then it gets executed earlier than expected.
+
+	  This patch fixes remove_from_queue function to also remove the entry
+	  from the dirlist.
+
+	  ASTERISK-17069 #close
+	  Reported by: Jeremy Kister
+
+	  ASTERISK-24442 #close
+	  Reported by: tootai
+
+	  Change-Id: If9ec9b88073661ce485d6b008fd0b2612e49a28b
+
+2015-05-08 14:47 +0000 [4dbd4021c9]  Rusty Newton <rnewton at digium.com>
+
+	* configs/basic-pbx: Modified main IVR to play new Allison prompt.
+
+	  The main IVR was playing demo-congrats. I've switched it over to the
+	  basic-pbx-ivr-main file that we added in core sounds 1.4.27. This prompt
+	  has Allison prompting the user with the actual IVR menu.
+
+	  ASTERISK-24892 #close
+
+	  Change-Id: Ifb749616ff8e156a1031ddaddfcc9244767a095d
+
+2015-05-08 15:55 +0000 [7111ba6df4]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "tcptls: Avoiding ERR_remove_state in OpenSSL." into 13
+2015-05-08 10:39 +0000 [613a461c3d]  Sean Bright <sean at malleable.com>
+
+	* res_rtp_asterisk: Issue ERROR if res_srtp is not found.
+
+	  While trying to get WebRTC working with chan_pjsip, I was running
+	  into the following error:
+
+	      Attempted to set an invalid DTLS-SRTP configuration on RTP
+	      instance...
+
+	  Josh helpfully pointed out that res_srtp.so might not be loaded, and
+	  sure enough, it wasn't. This patch adds a ERROR indiciating as much
+	  to hopefully help others having a similar problem.
+
+	  Change-Id: I13aa477b47b299876728a21b130998a0ea6cd19f
+
+2015-05-07 17:49 +0000 [394fcb5eab]  Rusty Newton <rnewton at digium.com>
+
+	* sounds: Add Swedish sounds to Makefile and XML
+
+	  Added the necessary lines to the Makefile and sounds.xml so we'll have the
+	  Swedish sounds in all available formats in menuselect.
+
+	  See also: Swedish sounds were added into the core sounds release 1.4.27.
+
+	  ASTERISK-24744 #close
+
+	  Reported by: Tove Hjelm
+	  Tested by: Rusty Newton
+
+	  Change-Id: Ib6f4fd177afd1667b2402735034001d4d055a908
+
+2015-05-08 09:54 +0000 [30c3b254c5]  Joshua Colp <jcolp at digium.com>
+
+	* Merge "doc: Make progdocs play nice with git" into 13
+2015-05-05 11:35 +0000 [2115f11b54]  Alexander Traud (License 6520)
+
+	* tcptls: Avoiding ERR_remove_state in OpenSSL.
+
+	  ERR_remove_state was deprecated with OpenSSL 1.0.0 and was replaced by 
+	  ERR_remove_thread_state. ERR_load_SSL_strings and ERR_load_BIO_strings were 
+	  called by SSL_load_error_strings already and got removed. These changes allow 
+	  OpenSSL forks like BoringSSL to be used with Asterisk.
+
+	  ASTERISK-25043 #close
+	  Reported by: Alexander Traud
+	  patches:
+	    asterisk_with_BoringSSL.patch uploaded by Alexander Traud (License 6520)
+
+	  Change-Id: If1c0871ece21a7e0763fafbd2fa023ae49d4d629
+	  (cherry picked from commit 247fef66537b59649e7571d64e2c574a106dbd65)
+
+2015-05-07 14:54 +0000 [5392e970d0]  George Joseph <george.joseph at fairview5.com>
+
+	* doc: Make progdocs play nice with git
+
+	  Moved contrib/asterisk-ng-doxygen to doc/asterisk-ng-doxygen.in
+
+	  Changed /Makefile to copy asterisk-ng-doxygen.in to
+	  asterisk-ng-doxygen then modify it with version instead of
+	  modifying asterisk-ng-doxygen directly.  Updated clean
+	  targets as well.
+
+	  Updated /.gitignore and doc/.gitignore.
+
+	  Change-Id: I38712d3e334fa4baec19d30d05de8c6f28137622
+
+2015-05-07 15:10 +0000 [1e44d1bef9]  Joshua Colp <jcolp at digium.com>
+
+	* Merge "res_pjsip_exten_state: Fix race condition between sending NOTIFY and termination" into 13
+2015-05-04 14:43 +0000 [608f0a94ee]  Ivan Poddubny <ivan.poddubny at gmail.com>
+
+	* contrib/editors: Fix vim syntax highlighting of comments in config files
+
+	   * Added a lookbehind to one-line comment matcher to skip escaped
+	     semicolons.
+	   * Added support for block comments.
+
+	  Change-Id: Id17dfaeda8ed4be572e8107a0c010066584aaee7
+
+2015-05-07 13:30 +0000 [22c6c12af2]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "vector:  Additional enhancements and fixes" into 13
+2015-05-06 13:24 +0000 [d649d682c4]  Joshua Colp <jcolp at digium.com>
+
+	* res_pjsip_exten_state: Fix race condition between sending NOTIFY and termination
+
+	  The res_pjsip_exten_state module currently has a race condition between
+	  processing the extension state callback from the PBX core and processing
+	  the subscription shutdown callback from res_pjsip_pubsub. There is currently
+	  no synchronization between the two. This can present a problem as while
+	  the SIP subscription will remain valid the tree it points to may not.
+	  This is in particular a problem as a task to send a NOTIFY may get queued
+	  which will try to use the tree that may no longer be valid.
+
+	  This change does the following to fix this problem:
+
+	  1. All access to the subscription tree is done within the task that
+	  sends the NOTIFY to ensure that no other thread is modifying or
+	  destroying the tree. This task executes on the serializer for the
+	  subscriptions.
+
+	  2. A reference to the subscription serializer is kept to ensure it
+	  remains valid for the lifetime of the extension state subscription.
+
+	  3. The NOTIFY task has been changed so it will no longer attempt
+	  to send a NOTIFY if the subscription has already been terminated.
+
+	  ASTERISK-25057 #close
+	  Reported by: Matt Jordan
+
+	  Change-Id: I0b3cd2fac5be8d9b3dc5e693aaa79846eeaf5643
+
+2015-05-07 07:02 +0000 [9322bc6ff6]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "chan_dahdi: Improve force_restart_unavailable_chans option description." into 13
+2015-05-07 06:39 +0000 [b1514362ef]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "res_stasis_snoop: Spying on a single direction continually increases CPU" into 13
+2015-05-07 06:28 +0000 [652ee2ff83]  Joshua Colp <jcolp at digium.com>
+
+	* Merge "features: Fix crash when transferee hangs up during DTMF attended transfer." into 13
+2015-05-05 20:22 +0000 [5f9aea8e3c]  George Joseph <george.joseph at fairview5.com>
+
+	* vector:  Additional enhancements and fixes
+
+	  After using the new vector stuff for real I found...
+
+	  A bug in AST_VECTOR_INSERT_AT that could cause a seg fault.
+
+	  The callbacks needed to be closer to ao2_callback in behavior
+	  WRT to CMP_MATCH and CMP_STOP behavior and the ability to return
+	  a vector of matched entries.
+
+	  A pre-existing issue with APPEND and REPLACE was also fixed.
+
+	  I also added a new macro to test.h that acts like ast_test_validate
+	  but also accepts a return code variable and a cleanup label.  As well
+	  as printing the error, it sets the rc variable to AST_TEST_FAIL and
+	  does a goto to the specified label on error.  I had a local version
+	  of this in test_vector so I just moved it.
+
+	  ASTERISK-25045
+
+	  Change-Id: I05e5e47fd02f61964be13b7e8942bab5d61b29cc
+
+2015-05-04 17:28 +0000 [68513e00f7]  Kevin Harwell <kharwell at digium.com>
+
+	* res_stasis_snoop: Spying on a single direction continually increases CPU
+
+	  Creating a snoop channel in ARI and spying only on a single direction (in or
+	  out) results in CPU utilization continually increasing until the CPU is fully
+	  consumed. This occurs because frames are being put in the opposing direction's
+	  slin factory queue, but not being removed.
+
+	  Fixed the problem by always reading and disposing of frames from the opposite
+	  queue of the direction selected.
+
+	  ASTERISK-24938 #closes
+
+	  Change-Id: I935bfd15f1db958f364d9d6b3b45582c0113dd60
+2015-05-06 16:00 +0000 [904f5d98f6]  Richard Mudgett <rmudgett at digium.com>
+
+	* chan_dahdi: Improve force_restart_unavailable_chans option description.
+
+	  ASTERISK-25034
+	  Reported by: Richard Mudgett
+
+	  Change-Id: I1ff8f02124d2f4abd632a050da52c64285bb7f30
+
+2015-05-06 07:42 +0000 [d6ffbe39b0]  Joshua Colp <jcolp at digium.com>
+
+	* Merge "app_queue: Fix queue_log EXITWITHTIMEOUT containing only 1 parameter" into 13
+2015-05-06 06:13 +0000 [dfb292ce3e]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "res_ari_bridges: Add missing dependencies." into 13
+2015-05-05 21:05 +0000 [50e90f9121]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "pbx_config: Register manager actions with module version of macro." into 13
+2015-05-05 18:17 +0000 [be1260a35f]  Richard Mudgett <rmudgett at digium.com>
+
+	* features: Fix crash when transferee hangs up during DTMF attended transfer.
+
+	  A crash happens with this sequence of steps:
+	  1) Party A is connected to party B.
+	  2) Party B starts a DTMF attended transfer.
+	  3) Party A hangs up while party B is dialing party C.
+
+	  When party A hangs up the bridge that party A and party B are in is
+	  dissolved and party B is kicked out of the bridge.  When party B finishes
+	  dialing party C he attempts to move to the new bridge with party C.  Since
+	  party B is no longer in a bridge the attempted move dereferences a NULL
+	  bridge_channel pointer and crashes.
+
+	  * Made the hold(), unhold(), ringing(), and the bridge_move() functions
+	  tolerant of the channel not being in a bridge.  The assertion that party B
+	  is always in a bridge is not true if the bridged peer of party B hangs up
+	  and dissolves the bridge.  Being tolerant of not being in a bridge allows
+	  the peer hangup stimulus to be processed by the FSM.
+
+	  * Made the bridge_move() function return void since where the return value
+	  for a failed move was checked generated a FSM coding ERROR message for a
+	  normal off-nominal condition.
+
+	  * Eliminated most uses of RAII_VAR in bridge_basic.c.
+
+	  ASTERISK-25003 #close
+	  Reported by: Artem Volodin
+
+	  Change-Id: Ie2c1b14e5e647d4ea6de300bf56d69805d7bcada
+
+2015-05-05 15:40 +0000 [8b0f85ac06]  George Joseph <george.joseph at fairview5.com>
+
+	* test_vector: Fix build breakage caused by ASTERISK_REGISTER_FILE
+
+	  My 13 version of test_vector had an ASTERISK_REGISTER_FILE() macro
+	  call at the top which is only supported in master.  Once removed
+	  builds are successful.
+
+	  Change-Id: I7cac8b669bed6de543bbf4e2eec3cffc9741acdd
+
+2015-05-05 14:48 +0000 [87263b47b5]  Ivan Poddubny <ivan.poddubny at gmail.com>
+
+	* app_queue: Fix queue_log EXITWITHTIMEOUT containing only 1 parameter
+
+	  This patch fixes EXITWITHTIMEOUT queue_log entry to always come with 3
+	  parameters: position, original position and waiting time.
+
+	  ASTERISK-25038 #close
+	  Reported by: Etienne Lessard
+
+	  Change-Id: I0c62045922e26bee2125e93aee1dee17eee79618
+
+2015-05-05 13:13 +0000 [2d9081b5ec]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "stasis: Fix dial masquerade datastore lifetime" into 13
+2015-05-05 12:45 +0000 [8ca25dfd7e]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "vector:  Traversal, retrieval, insert and locking enhancements" into 13
+2015-05-05 09:47 +0000 [366ea63438]  Corey Farrell <git at cfware.com>
+
+	* res_ari_bridges: Add missing dependencies.
+
+	  Missed this module in the previous commit.  res_ari_bridges uses symbols
+	  from res_stasis_playback and res_stasis_recording.
+
+	  ASTERISK-25027 #close
+	  Reported by: Corey Farrell
+
+	  Change-Id: I90bf756abd25adfc4920d2869ebe7feb636b8c5f
+
+2015-05-05 09:27 +0000 [69ae8cf0a4]  Corey Farrell <git at cfware.com>
+
+	* pbx_config: Register manager actions with module version of macro.
+
+	  Switch manager actions in pbx_config to use the registration macro that
+	  passes the module pointer, allowing pbx_config reference to be bumped
+	  while the manager actions run.
+
+	  ASTERISK-25061 #close
+	  Reported by: Corey Farrell
+
+	  Change-Id: I422c50dd74814616ac10c5e9c6598a0b1bc2c44e
+
+2015-05-04 12:16 +0000 [181ae3b8d9]  Joshua Colp <jcolp at digium.com>
+
+	* stasis: Fix dial masquerade datastore lifetime
+
+	  A recent change went into Asterisk which added reference counts to the
+	  channels stored in a dial masquerade datastore. Unfortunately this
+	  included a reference to the caller in a dialing operation. While all
+	  of the dialed targets have the datastore removed from them upon dialing
+	  completion this did not occur for the caller, causing it to have a
+	  reference to itself that could go never go away (as it depended on
+	  the destruction of the datastore which only happened when the channel
+	  was destroyed). This resulted in the caller channel remaining on the
+	  system despite it having hung up.
+
+	  This change does the following to fix this issue:
+
+	  1. The dial masquerade datastore is now removed from the caller upon
+	  dialing completion, just like the dialed targets.
+	  2. Upon destruction of the caller all the dialed targets are also
+	  removed from the dial masquerade datastore (just in case).
+	  3. The reference to the caller has been removed as it should not be
+	  possible for the datastore to now be valid/useful after the lifetime
+	  of the caller has ended.
+
+	  ASTERISK-25025 #close
+
+	  Change-Id: I1ef4ca5ca04980028604cc2af5d2992ac3431b3f
+
+2015-05-01 19:25 +0000 [7a7e9733c2]  George Joseph <george.joseph at fairview5.com>
+
+	* vector:  Traversal, retrieval, insert and locking enhancements
+
+	  Renamed AST_VECTOR_INSERT to AST_VECTOR_REPLACE because it really
+	  does replace not insert.  The few users of AST_VECTOR_INSERT were
+	  refactored.  Because these are macros, there should be no ABI
+	  compatibility issues.
+
+	  Added AST_VECTOR_INSERT_AT that actually inserts an element into the
+	  vector at a specific index pushing existing elements to the right.
+
+	  Added AST_VECTOR_GET_CMP that can retrieve from the vector based
+	  on a user-provided compare function.
+
+	  Added AST_VECTOR_CALLBACK function that will execute a function
+	  for each element in the vector.  Similar to ao2_callback and
+	  ao2_callback_data functions although the vector callback can take
+	  a variable number of arguments.  This should allow easy migration
+	  to a vector where a container might be too heavy.
+
+	  Added read/write locked vector and lock manipulation macros.
+
+	  Added unit tests.
+
+	  ASTERISK-25045 #close
+
+	  Change-Id: I2e07ecc709d2f5f91bcab8904e5e9340609b00e0
+
+2015-05-03 13:55 +0000 [040d2f8558]  Corey Farrell <git at cfware.com>
+
+	* main/test.c: Add test to verify there were no registration errors.
+
+	  This adds a test that will fail if any test failed to register. Also fail
+	  if any test registration produced a warning about missing a leading or
+	  trailing slash.
+
+	  ASTERISK-25053 #close
+	  Reported by: Corey Farrell
+
+	  Change-Id: I93e50b8fcbcfa7f1f5b41b2c44a51685c09529c3
+
+2015-05-04 09:26 +0000 [626bffc4c2]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "contrib/ast-db-manage: Add Postgres ENUM type support in auto DTMF mode update" into 13
+2015-05-04 09:26 +0000 [87fb7fc165]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "cdr/cdr_csv.c: Add a new option to enable columns added in Asterisk 1.8" into 13
+2015-05-04 09:25 +0000 [81c27127aa]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "Format Interfaces: Prevent unload except by shutdown." into 13
+2015-05-04 07:46 +0000 [743fed71fc]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "res_odbc: Use negative connection cache for all connections" into 13
+2015-04-21 11:52 +0000 [3dcec04ab5]  Martin Tomec <tomec.martin at gmail.com>
+
+	* res_odbc: Use negative connection cache for all connections
+
+	  Apply the negative connection cache setting to all connections,
+	  even those that are not pooled. This ensures that the connection
+	  will not be re-established before the negative connection cache
+	  time is met.
+
+	  ASTERISK-22708 #close
+
+	  Change-Id: I431cc2e8584ab0b6908b3523d0a0e18c9a527271
+2015-05-04 04:03 +0000 [74799b3fe2]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "Remove unneeded uses of optional_api providers." into 13
+2015-05-04 04:03 +0000 [78c02f8e88]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "Update configure.ac/Makefile for clang" into 13
+2015-05-03 21:03 +0000 [f38066fcad]  Corey Farrell <git at cfware.com>
+
+	* Format Interfaces: Prevent unload except by shutdown.
+
+	  Format interfaces cannot be unregistered, so the modules that provide them
+	  need to be held open except by shutdown.
+
+	  ASTERISK-25054 #close
+	  Reported by: Corey Farrell
+
+	  Change-Id: Iadbd9675bf0d30b8fded5a739b163db3ea2db8f3
+
+2015-05-03 20:28 +0000 [e76a6a97bf]  Matt Jordan <mjordan at digium.com>
+
+	* contrib/ast-db-manage: Add Postgres ENUM type support in auto DTMF mode update
+
+	  The upgrade script for auto DTMF mode (31cd4f4891ec) added in 88b0fa7755
+	  failed to add ENUM support for Postgres databases. This requires a
+	  specific import from the sqlalchemy.dialects.postgresql package. This
+	  patch corrects this error, which allows for Postgres update scripts to
+	  be generated.
+
+	  ASTERISK-24706
+
+	  Change-Id: I4742ac8efa533cd6f18e0bdd907b339a9aedf015
+
+2015-05-01 19:50 +0000 [92120247e9]  D Tucny <d at tucny.com>
+
+	* term: send proper reset sequence when black background is forced
+
+	  When using the force black background command-line option or configuration
+	  option an invalid reset sequence is sent following a coloured output item 
+	  in the CLI, the result is that the colour is not 'turned off' and continues
+	  until the next non-default coloured text output.
+
+	  A reset sequence is already defined in term.c, but the ast_term_reset
+	  function doesn't use it, instead building it's own invalid sequence and 
+	  returning that.
+
+	  This patch changes that behaviour, removing the building of a reset sequence
+	  and instead using the pre-built constant 'enddata' which is a suitable reset
+	  sequence for this purpose.
+
+	  ASTERISK-24896 #close
+	  Reported by: Dan Tucny
+
+	  Change-Id: I56323899123ae3264900389cae1f5b252aa3bf43
+
+2015-05-03 09:20 +0000 [13819a34c4]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "Build System: Prevent unneeded changes to asterisk/buildopts.h." into 13
+2015-05-03 09:19 +0000 [b518ba1c6c]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "res_pjsip_dlg_options: Fix MODULEINFO section." into 13
+2015-05-02 18:58 +0000 [ad6ea29697]  Corey Farrell <git at cfware.com>
+
+	* Remove unneeded uses of optional_api providers.
+
+	  A few cases exist where headers of optional_api provders are included but
+	  not needed.  This causes unneeded calls to ast_optional_api_use.
+
+	  * Don't include optional_api.h from sip_api.h.
+	  * Move 'struct ast_channel_monitor' to channel.h.
+	  * Don't include monitor.h from chan_sip.c, channel.c or features.c.
+
+	  The move of struct ast_channel_monitor is needed since channel.c depends on
+	  it.  This has no effect on users of monitor.h since channel.h is included
+	  from monitor.h.
+
+	  ASTERISK-25051 #close
+	  Reported by: Corey Farrell
+
+	  Change-Id: I53ea65a9fc9693c89f8bcfd6120649bfcfbc3478
+
+2015-05-02 10:19 +0000 [9888562c8c]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "include/asterisk/channel.h: Fix typo" into 13
+2015-05-02 10:17 +0000 [b4000f2d44]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "Astobj2: Fix initialization order of refdebug and AO2_DEBUG." into 13
+2015-04-30 02:07 +0000 [525c8c8689]  Rodrigo Ramírez Norambuena <decipher.hk at gmail.com>
+
+	* include/asterisk/channel.h: Fix typo
+
+	  Change-Id: Ie584b85e16a94c255e60d0b1732ef9686464fef3
+
+2015-05-02 02:15 +0000 [63196a8256]  Corey Farrell <git at cfware.com>
+
+	* res_pjsip_dlg_options: Fix MODULEINFO section.
+
+	  Removed the extra space before "MODULEINFO" in res_pjsip_dlg_options.
+	  This extra space prevented any of the dependencies from being seen by
+	  menuselect, so building with default options would fail if PJSIP was
+	  not installed.
+
+	  This also makes the tool that extracts information for menuselect
+	  tolerant of multiple spaces in the future.
+
+	  ASTERISK-25033 #close
+	  Reported by: Peter Whisker
+
+	  Change-Id: Iccd54846f70c4a7a50cb5bf70b7bb5cb4bab3698
+
+2015-04-29 03:03 +0000 [ac1f0090eb]  Corey Farrell <git at cfware.com>
+
+	* Build System: Prevent unneeded changes to asterisk/buildopts.h.
+
+	  * Add AST_DEVMODE to BUILDOPTS
+	  * Remove CFLAGS that do not effect ABI from BUILDOPTS.
+	  * Use BUILDOPTS to generate AST_BUILDOPT_SUM.
+	  * Remove loop that defined AST_MODULE_*
+
+	  These changes ensure that only ABI effecting options are considered for
+	  AST_BUILDOPT_SUM.  This also reduces unneeded full system rebuilds caused
+	  by enabling or disabling one module that another is dependent on.
+
+	  ASTERISK-25028
+	  Reported by: Corey Farrell
+
+	  Change-Id: I2c516d93df9f6aaa09ae079a8168c887a6ff93a2
+
+2015-05-01 13:22 +0000 [5875bf183c]  Corey Farrell <git at cfware.com>
+
+	* Astobj2: Fix initialization order of refdebug and AO2_DEBUG.
+
+	  This ensures that refdebug is initialized before AO2_DEBUG if
+	  both are enabled, since AO2_DEBUG allocates a container.
+
+	  This change also makes AO2_DEBUG initialization critical, a
+	  failure will abort Asterisk startup.  This is needed since
+	  the failure would be caused by reg_containers allocation
+	  failure, and that would result in a segmentation fault by
+	  ao2_container_register later in startup.
+
+	  ASTERISK-25048 #close
+	  Reported by: Corey Farrell
+
+	  Change-Id: I9a243ea3fc5653b48b931ba6d61971cb2e530244
+
+2015-04-29 14:49 +0000 [1b19c15f17]  Matt Jordan <mjordan at digium.com>
+
+	* main/pbx: Improve performance of dialplan reloads with a large number of hints
+
+	  The PBX core maintains two hash tables for hints: a container of the
+	  actual hints (hints), along with a container of devices that are watching that
+	  hint (hintdevices). When a dialplan reload occurs, each hint in the hints
+	  container is destroyed; this requires a lookup in the container of devices to
+	  find the device => hint mapping object. In the current code, this performs an
+	  ao2_callback, iterating over each of the device to hint objects in the
+	  hintdevices container. For a large number of hints, this is extremely
+	  expensive: dialplan reloads with 20000 hints could take several minutes
+	  in just this phase.
+
+	  This patch improves the performance of this step in the dialplan reloads
+	  by caching which devices are watching a hint on the hint object itself.
+	  Since we don't want to create a circular reference, we just cache the
+	  name of the device. This allows us to perform a smarter ao2_callback on
+	  the hintdevices container during hint removal, hashing on the name of the
+	  device and returning an iterator to the matching names. The overall
+	  performance improvement is rather large, taking this step down to a number of
+	  seconds as opposed to minutes.
+
+	  In addition, this patch also registers the hint containers in the PBX
+	  core with the astobj2 library. This allows for reasonable debugging to
+	  hash collisions in those containers.
+
+	  ASTERISK-25040 #close
+	  Reported by: Matt Jordan
+
+	  Change-Id: Iedfc97a69d21070c50fca42275d7b3e714e59360
+
+2015-05-01 06:55 +0000 [ec0f80b6e8]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "res_pjsip_outbound_authenticator_digest: Add missing outbound authenticator callback." into 13
+2015-05-01 06:55 +0000 [ed51fbbe9c]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "Prevent potential crash on blond transfer." into 13
+2015-04-30 15:54 +0000 [3efe0df044]  Corey Farrell <git at cfware.com>
+
+	* Sample Configs: Fix syntax error in pjsip.conf
+
+	  The sample pjsip.conf has a few comment lines that are missing the
+	  semicolons at the start of the comment, causing the config to fail
+	  load.
+
+	  Change-Id: I776a38c916a7df7ee3e072fd0b21dbf4cc457352
+
+2015-04-30 15:20 +0000 [077979618b]  Mark Michelson <mmichelson at digium.com>
+
+	* Prevent potential crash on blond transfer.
+
+	  Scenario:
+	  Alice calls Bob. Bob performs a blond transfer to Carol. Carol rejects
+	  the incoming call (or some other immediate circumstance causes Carol not
+	  to answer the call)
+
+	  What occurs in this case is that when the bridge between Alice and Bob
+	  breaks, Alice is told to masquerade into Bob's channel that had placed
+	  the call to Carol. The actual masquerade goes down without a hitch.
+	  However, a channel fixup callback that attempts to publish dial events
+	  over Stasis has a crash. The reason for this crash is that the datastore
+	  on Bob's channel that placed the outbound call to Carol only had a bare
+	  pointer to Carol's channel. Since Carol rejected the incoming call,
+	  Carol's channel has been hung up and freed, meaning accessing her
+	  channel results in a crash.
+
+	  The fix here is simple. The dial fixup code has been altered to hold
+	  references to the involved channels and to drop those references when
+	  freeing data.
+
+	  ASTERISK-25025 #close
+	  Reported by Chet Stevens
+
+	  Change-Id: I54eedda207b8ec7a69263353b43abe5746aea197
+
+2015-04-30 14:09 +0000 [4b8cddfb36]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip_outbound_authenticator_digest: Add missing outbound authenticator callback.
+
+	  The Asterisk 13 version of the fix for outbound registration was missing
+	  a key component that set the outbound authenticator's callback that
+	  creates an authenticated request based on an old request. This was
+	  picked up by some outbound registration tests failing in the testsuite.
+
+	  Change-Id: I5ca9379698c606da36bc38eaffccedaf64211ce3
+2015-04-30 13:42 +0000 [415a0d0745]  Joshua Colp <jcolp at digium.com>
+
+	* res_ari_device_states: Fix dependency on res_stasis_device_state.
+
+	  The res_ari_device_states module depends on res_stasis_device_state,
+	  not res_stasis_device_states.
+
+	  Change-Id: I26e02ad37f9e36bcc859867e2fad1b90452ec3de
+
+2015-04-30 11:11 +0000 [e0c6f88010]  Mark Michelson <mmichelson at digium.com>
+
+	* Merge "chan_dahdi: Add the chan_dahdi.conf force_restart_unavailable_chans option." into 13
+2015-04-30 10:53 +0000 [d1bc86fc99]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "res_pjsip_outbound_registration: Add virtual line support." into 13
+2015-04-29 14:29 +0000 [d3c310a28c]  Richard Mudgett <rmudgett at digium.com>
+
+	* chan_dahdi: Add the chan_dahdi.conf force_restart_unavailable_chans option.
+
+	  Some telco switches occasionally ignore ISDN RESTART requests.  The fix
+	  for ASTERISK-19608 added an escape clause for B channels in the restarting
+	  state if the telco ignores a RESTART request.  If the telco fails to
+	  acknowledge the RESTART then Asterisk will assume the telco acknowledged
+	  the RESTART on the second call attempt requesting the B channel by the
+	  telco.  The escape clause is good for dealing with RESTART requests in
+	  general but it does cause the next call for the restarting B channel to be
+	  rejected if the telco insists the call must go on that B channel.
+
+	  chan_dahdi doesn't really need to issue a RESTART request in response to
+	  receiving a cause 44 (Requested channel not available) code.  Sending the
+	  RESTART in such a situation is not required (nor prohibited) by the
+	  standards.  I think chan_dahdi does this for historical reasons to deal
+	  with buggy peers to get channels unstuck in a similar fashion as the
+	  chan_dahdi.conf resetinterval option.
+
+	  * Add the chan_dahdi.conf force_restart_unavailable_chans compatability
+	  option that when disabled will prevent chan_dahdi from trying to RESTART
+	  the channel in response to a cause 44 code.
+
+	  ASTERISK-25034 #close
+	  Reported by: Richard Mudgett
+
+	  Change-Id: Ib8b17a438799920f4a2038826ff99a1884042f65
+2015-04-30 06:38 +0000 [7f611fa0e8]  Rodrigo Ramírez Norambuena <decipher.hk at gmail.com>
+
+	* cdr/cdr_csv.c: Add a new option to enable columns added in Asterisk 1.8
+
+	  This patch adds a new option to cdr.conf, 'newcdrcolumns', that will handle CDR
+	  columns added in Asterisk 1.8. The columns are:
+	   * peeraccount
+	   * linkedid
+	   * sequence
+	  When enabled, the columns in the database entry will be populated with the data
+	  from the CDR.
+
+	  ASTERISK-24976 #close
+
+	  Change-Id: I51a57063f4ae5e194a9d933a8df45dc8a4534f0b
+
+2015-04-30 06:04 +0000 [e332c7ed5e]  Joshua Colp <jcolp at digium.com>
+
+	* res_pjsip_outbound_registration: Fix double unref on error return.
+
+	  When the PJSIP pjsip_regc_send function is invoked and an error
+	  status returned the caller currently decrements the reference count
+	  of the client state that it just incremented, assuming the
+	  registration callback would not have been invoked. In practice
+	  this is not correct. If the failure happens after the transaction
+	  has been set up the callback will still be invoked. This will
+	  cause the reference count to be incorrectly decremented twice, once
+	  by the registration callback and second by the caller of
+	  pjsip_regc_send.
+
+	  This change makes it so that whether the callback is invoked or
+	  not is known by the caller of pjsip_regc_send. Depending on
+	  this it can know whether it is responsible for decrementing the
+	  reference count of the client state or not.
+
+	  ASTERISK-25037 #close
+	  Reported by: Joshua Colp
+
+	  Change-Id: I749dc12f3a22115c49c5d7d95ff42a5fa45319de
+
+2015-04-20 13:03 +0000 [9c3ed42875]  Diederik de Groot <ddegroot at talon.nl>
+
+	* Update configure.ac/Makefile for clang
+
+	  Created autoconf/ast_check_raii.m4: contains AST_CHECK_RAII which
+	  checks compiler requirements for RAII:
+	  gcc: -fnested-functions support
+	  clang: -fblocks (and if required -lBlocksRuntime)
+	  The original check was implemented in configure.ac and now has it's
+	  own file. This function also sets C_COMPILER_FAMILY to either gcc or
+	  clang for use by makefile
+
+	  Created autoconf/ast_check_strsep_array_bounds.m4 (contains
+	  AST_CHECK_STRSEP_ARRAY_BOUNDS):
+	  which checks if clang is able to handle the optimized strsep & strcmp
+	  functions (linux). If not, the standard libc implementation should be
+	  used instead. Clang + the optimized macro's work with:
+	  strsep(char *, char []), but not with strsepo(char *, char *).
+	  Instead of replacing all the occurences throughout the source code,
+	  not using the optimized macro version seemed easier
+
+	  See 'define __strcmp_gc(s1, s2, l2) in bits/string2.h':
+	  llvm-comment: Normally, this array-bounds warning are suppressed for
+	  macros, so that unused paths like the one that accesses __s1[3] are
+	  not warned about.  But if you preprocess manually, and feed the
+	  result to another instance of clang, it will warn about all the
+	  possible forks of this particular if statement. Instead of switching
+	  of this optimization, another solution would be to run the preproces-
+	  sing step with -frewrite-includes, which should preserve enough
+	  information so that clang should still be able to suppress the diag-
+	  nostic at the compile step later on.
+
+	  See also "https://llvm.org/bugs/show_bug.cgi?id=20144"
+	  See also "https://llvm.org/bugs/show_bug.cgi?id=11536"
+
+	  Makefile.rules: If C_COMPILER_FAMILY=clang then add two warning
+	  suppressions:
+	  -Wno-unused-value
+	  -Wno-parentheses-equality
+	  In an earlier review (reviewboard: 4550 and 4554), they were deemed a
+	  nuisace and less than benefitial.
+
+	  configure.ac:
+	  Added AST_CHECK_RAII() see earlier
+	  Added AST_CHECK_STRSEP_ARRAY_BOUNDS() see earlier
+	  Removed moved content
+
+	  ASTERISK-24917
+	  Change-Id: I12ea29d3bda2254ad3908e279b7effbbac6a97cb
+
+2015-04-29 16:43 +0000 [37a193da18]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "ARI: Fix missing dependencies." into 13
+2015-04-29 16:42 +0000 [6a86b3555b]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "res_fax: allow 2400 transmission rate according to v.27ter standard" into 13
+2015-04-29 16:15 +0000 [d4e207e27e]  Matt Jordan <mjordan at digium.com>
+
+	* main/rtp_engine: Fix DTLS double-free introduced by 0b6410c4f8
+
+	  The patch in 0b6410c4f8 did correctly fix a memory leak of the DTLS
+	  structures in the RTP engine. However, when a 'core reload' is issued, a
+	  double free of the memory pointed to by the char *'s in the DTLS
+	  configuration struct can occur, as ast_rtp_dtls_cfg_free does not set
+	  the pointers to NULL when they are freed.
+
+	  This patch sets those pointers to NULL, preventing a second call to
+	  ast_rtp_dtls_cfg_free from corrupting memory.
+
+	  ASTERISK-25022
+
+	  Change-Id: I820471e6070a37e3c26f760118c86770e12f6115
+
+2015-04-29 13:05 +0000 [3fb6daeb55]  Kevin Harwell <kharwell at digium.com>
+
+	* res_fax: allow 2400 transmission rate according to v.27ter standard
+
+	  A previous set of patches (see: ASTERISK-22790 & ASTERISK-23231) made it so
+	  a v.27 modem was not allowed to have a minimum transmission rate of 2400 bits
+	  per second. This reverts all or some of those patches since according to the
+	  v.27ter standard a rate of 2400 bits per second is also supported.
+
+	  One of the original patches also added 9600 bits per second support for v.27.
+	  This patch also removes that since v.27ter only supports 2400/4800 bits per
+	  second.
+
+	  Also, since Asterisk specifically supports v.27ter the enum was renamed to
+	  better reflect this.
+
+	  ASTERISK-24955 #close
+	  Reported by: Matt Jordan
+
+	  Change-Id: I4b9dfb6bf7eff08463ab47ee1a74224f27cae733
+
+2015-04-29 10:46 +0000 [49ef81c15c]  Joshua Colp <jcolp at digium.com>
+
+	* res_sorcery_config: Fix build issue due to syntax error.
+
+	  Change-Id: Ic8322f04e37842848ad72cf2871bd0378f67c4ac
+
+2015-04-28 00:29 +0000 [3278fe5327]  Ashley Sanders <asanders at digium.com>
+
+	* chan_pjsip: Creating Channel Causes Asterisk to Crash When Duplicate AOR
+	              Sections Exist in pjsip.conf
+
+	  This patch modifies the current loading strategy of the pjsip configuration. If
+	  duplicate sections (e.g. sections containing the same [id/type]) are defined in
+	  [pjsip.conf], the loader will consider the configuration for the given type as
+	  invalid when the duplicate section is encountered. The entire configuration
+	  (including what was previously loaded) for the duplicate [id/type] sections
+	  will be rejected and destroyed, an error message is logged and the load
+	  processing for the given stops.
+
+	  ASTERISK-24996
+	  Reported By: Ashley Sanders
+
+	  Change-Id: I35090ca4cd40f1f34881dfe701a329145c347aef
+
+2014-11-04 06:03 +0000 [89f6719f7a]  Joshua Colp <jcolp at digium.com>
+
+	* res_pjsip_outbound_registration: Add virtual line support.
+
+	  Virtual line support establishes a relationship between messages
+	  related to an outbound registration and a local endpoint. This is
+	  accomplished by attaching a parameter to the Contact of the outbound
+	  registration and looking for it on any received requests. If the
+	  parameter exists and can be matched to an outbound registration
+	  the configured endpoint is associated with the request.
+
+	  ASTERISK-24949 #close
+	  Reported by: Joshua Colp
+
+	  Change-Id: I7df909d2625479110a83fdd354c21ac539e8615d
+
+2015-04-29 06:39 +0000 [d61f03c4f9]  Corey Farrell <git at cfware.com>
+
+	* ARI: Fix missing dependencies.
+
+	  ARI modules that are generated by 'make ari-stubs' are all dependent on
+	  res_ari_model.  Additionally some of the same modules depend on one or more
+	  res_stasis_* modules.
+
+	  ASTERISK-25027 #close
+	  Reported by: Corey Farrell
+
+	  Change-Id: I8e07fe7e81fedacb87232f2b6f8b5f47927b4153
+
+2015-04-29 06:26 +0000 [3e4624ad21]  Corey Farrell <git at cfware.com>
+
+	* res_pjsip: Remove incorrect MODULEINFO from presence_xml.c.
+
+	  Remove incorrect MODULEINFO block and unneeded header includes
+	  from presence_xml.c.
+
+	  ASTERISK-25027
+	  Reported by: Corey Farrell
+
+	  Change-Id: I977c609ab9d1fe05373027c4138900f6985990eb
+
+2015-04-29 06:17 +0000 [fed9faab8d]  Corey Farrell <git at cfware.com>
+
+	* Git Migration: Create doc/rest-api when needed.
+
+	  Create the directory './doc/rest-api' at the start of 'make ari-stubs'
+	  to prevent an error when documentation is generated.  The directory is
+	  also added to git ignores.
+
+	  ASTERISK-25027
+	  Reported by: Corey Farrell
+
+	  Change-Id: Iaccc7f0138501c23aa78feaca2f3cce9e68cbc1b
+
+2015-04-29 05:17 +0000 [df23c8a86b]  Joshua Colp <jcolp at digium.com>
+
+	* res_pjsip_outbound_registration: Fix build due to removal of transaction.
+
+	  Change-Id: I7a8a7beec3334cec304943f2dd7597eabe2e3150
+
+2015-04-28 19:18 +0000 [95ab9fdb1a]  Joshua Colp <jcolp at digium.com>
+
+	* Merge "res_pjsip_outbound_registration: Add debugging messages." into 13
+2015-04-28 19:18 +0000 [0e70dc0dc8]  Joshua Colp <jcolp at digium.com>
+
+	* Merge "res_pjsip_outbound_registration: Don't fail on delayed processing: 13." into 13
+2015-04-27 16:56 +0000 [e39bd6ba46]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip_outbound_registration: Don't fail on delayed processing: 13.
+
+	  This is the Asterisk 13 version of a change to master that allows for
+	  registration responses to be processed successfully potentially after
+	  the original transaction has timed out. The main difference between this
+	  and the master change is that the master version has API changes that
+	  are unacceptable for 13. For 13, this is worked around by adding a new
+	  API call that the outbound registration code uses instead.
+
+	  The following is the text from the master version of this commit:
+
+	  Odd behaviors have been observed during outbound registrations. The most
+	  common problem witnessed has been one where a request with
+	  authentication credentials cannot be created after receiving a 401
+	  response. Other behaviors include apparently processing an incorrect SIP
+	  response.
+
+	  Inspecting the code led to an apparent issue with regards to how we
+	  handle transactions in outbound registration code. When a response to a
+	  REGISTER arrives, we save a pointer to the transaction and then push a
+	  task onto the registration serializer. Between the time that we save the
+	  pointer and push the task, it's possible for the transaction to be
+	  destroyed due to a timeout. It's also possible for the address to be
+	  reused by the transaction layer for a new transaction.
+
+	  To allow for authentication of a REGISTER request to be authenticated
+	  after the transaction has timed out, we now also hold a reference to the
+	  original REGISTER request instead of the transaction. The function for
+	  creating a request with authentication has been altered to take the
+	  original request instead of the transaction where the original request
+	  was sent.
+
+	  ASTERISK-25020
+	  Reported by Mark Michelson
+
+	  Change-Id: If1ee5f601be839479a219424f0358a229f358f7c
+2015-04-27 14:44 +0000 [1bf008fc76]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip_outbound_registration: Add debugging messages.
+
+	  When problems occur regarding outbound registrations, it currently
+	  is difficult to debug. Most off-nominal paths had warning messages,
+	  but sometimes we want to know what's going on before hitting the
+	  off-nominal path. This patch adds lots of debugging output that
+	  should give a clearer picture of what is happening with regards
+	  to outbound registrations.
+
+	  ASTERISK-25020
+	  Reported by Mark Michelson
+
+	  Change-Id: I577bde7860be0a6c872b5bcb4d5047340bf45d45
+
+2015-04-28 07:13 +0000 [7ee05892d6]  Joshua Colp <jcolp at digium.com>
+
+	* Merge "Example script for scan-build (the llvm static analyzer)" into 13
+2015-04-28 05:38 +0000 [0b6410c4f8]  Steve Davies <steve at one47.co.uk>
+
+	* res_rtp_asterisk: Resolve 2 discrete memory leaks in DTLS
+
+	  ao2 ref leak in res_rtp_asterisk.c when a DTLS policy is created.
+	  The resources are linked into a table, but the original alloc refs
+	  are never released. ast_strdup leak in rtp_engine.c. If
+	  ast_rtp_dtls_cfg_copy() is called twice on the same destination struct,
+	  a pointer to an alloc'd string is overwritten before the string is free'd.
+
+	  ASTERISK-25022
+	  Reported by: one47
+
+	  Change-Id: I62a8ceb8679709f6c3769136dc6aa9a68202ff9b
+
+2015-04-28 06:55 +0000 [427209603d]  Joshua Colp <jcolp at digium.com>
+
+	* Merge "cdr/cdr_odbc.c: Added to record new columns add on CDR 1.8 Asterisk Version" into 13
+2015-04-27 12:11 +0000 [99fb87ae13]  George Joseph <george.joseph at fairview5.com>
+
+	* res_pjsip: Fix SEGV on pending-qualify contacts
+
+	  Permanent contacts that hadn't been qualified yet were missing
+	  their contact_status entries causing SEGVs when running CLI
+	  commands.
+
+	  This patch makes sure that contact_statuses are created for
+	  both dynamic and permanent contacts when they are created.
+	  It also adds checks in the CLI code to make sure there's a
+	  contact_status, just in case.
+
+	  ASTERISK-25018 #close
+	  Reported-by: Ivan Poddubny
+	  Tested-by: Ivan Poddubny
+	  Tested-by: George Joseph
+
+	  Change-Id: I3cc13e5cedcafb24c400368b515b02d7fb81e029
+
+2015-04-15 18:55 +0000 [d5dd43856e]  Rodrigo Ramírez Norambuena <decipher.hk at gmail.com>
+
+	* cdr/cdr_odbc.c: Added to record new columns add on CDR 1.8 Asterisk Version
+
+	  Add new column to INSERT new columns added in cdr 1.8 version. The columns are:
+	   * peeraccount
+	   * linkedid
+	   * sequence
+	  This feature is configurable in cdr_odbc.conf using a new configuration
+	  option, 'newcdrcolumns'.
+
+	  ASTERISK-24976 #close
+
+	  Change-Id: Ibe0c7540a88305c6012786f438a0813ad8b19127
+2015-04-26 17:21 +0000 [e9788056e9]  Matt Jordan <mjordan at digium.com>
+
+	* channels/chan_skinny: Fix compilation error introduced in f8e21a1adf
+
+	  A typo in commit f8e21a1adf resulted in a compilation error in
+	  chan_skinny. This patch fixes the typo.
+
+	  ASTERISK-24917
+
+	  Change-Id: Id7f4ad1fe948eb2408622e80c27936ce4516c33c
+
+2015-04-26 15:53 +0000 [2d277996b7]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "Clang: Fix some more tautological-compare warnings." into 13
+2015-04-24 13:07 +0000 [145f65598c]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "app_confbridge: Default the template option to a compatible default profile." into 13
+2015-04-23 15:11 +0000 [7e5056b393]  Kevin Harwell <kharwell at digium.com>
+
+	* app_confbridge: Default the template option to a compatible default profile.
+
+	  Confbridge dynamic profiles did not have a default profile unless you
+	  explicitly used Set(CONFBRIDGE(bridge,template)=default_bridge). If a
+	  template was not set prior to the bridge being created then some
+	  options were left with no default values set. This patch makes it so
+	  the default templates are set to the default bridge and user profiles.
+
+	  ASTERISK-24749 #close
+	  Reported by: philippebolduc
+
+	  Change-Id: I1bd6e94b38701ac2112d842db68de63d46f60e0a
+
+2015-04-24 09:17 +0000 [1da9ec969d]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip_outbound_authenticator: Increase CSeq on authed requests.
+
+	  The way PJSIP generates an authenticated request is to use a previous
+	  request as a template. This means that the authenticated request will
+	  have the same Call-ID, From header (including tag), and CSeq as the
+	  original request. PJSIP generates a new branch on the Via header to
+	  indicate that this is a new transaction, though.
+
+	  There are some SIP implementations, though, that do not notice the
+	  change in the branch and therefore will match the authed request to the
+	  original request's transaction. Since the CSeq is the same, the server
+	  will repeat the response it sent to the original request.
+
+	  This patch aids interoperability by increasing the CSeq of the authed
+	  request by one.
+
+	  ASTERISK-24845 #close
+	  Reported by: Carl Fortin
+	  Tested by: Carl Fortin
+
+	  Change-Id: I39c4ca52e688a9f83bcc1878371334becdc5be01
+
+2015-04-24 09:24 +0000 [bf3d9db4a6]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "res_pjsip_t38: Don't crash on authenticated reinvite after originated T.38 FAX." into 13
+2015-04-20 13:06 +0000 [cb318f3960]  Diederik de Groot <ddegroot at talon.nl>
+
+	* Example script for scan-build (the llvm static analyzer)
+
+	   - Added Pre-amble (Options / Flags / Usage Example / GNU License)
+	   - Extended Configurability
+	   - Made Executable
+
+	  ASTERISK-24917
+	  Change-Id: I70405fe54e4be7dbfbcb62e291690069b88617a8
+
+2015-04-23 17:23 +0000 [b3cd5bc77f]  Mark Michelson <mmichelson at digium.com>
+
+	* Merge "Clang: change previous tautological-compare fixes." into 13
+2015-04-23 12:54 +0000 [eabf3b5a3c]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip_t38: Don't crash on authenticated reinvite after originated T.38 FAX.
+
+	  When Asterisk originates a channel to an application, the channel is
+	  hung up once the application finishes executing. When the application
+	  in question is SendFax, the Asterisk PJSIP code will attempt to reinvite
+	  the T.38 session to audio after the FAX completes. The hangup of the
+	  channel happens in the midst of this reinvite transaction. In most
+	  circumstances, this works out okay because the BYE is delayed until the
+	  reinvite transaction can complete.
+
+	  However, if the reinvite that Asterisk sends receives a 401/407
+	  response, then Asterisk's attempt to re-send the reinvite with
+	  authentication will fail. This is because the session supplement in
+	  res_pjsip_t38 makes the assumption that the channel on the session will
+	  always be non-NULL. Since the channel has been hung up, though, the
+	  channel is now NULL. Attempting to operate on the channel causes a
+	  crash.
+
+	  This patch fixes the issue by ensuring that the channel on the session
+	  is not NULL before attempting to mess with the T.38 framehook.
+
+	  This patch also contains some corrections for comments that were
+	  incorrect and really confused me when I first started looking at the
+	  code.
+
+	  ASTERISK-25004 #close
+	  Reported by Mark Michelson
+
+	  Change-Id: Ic5a1230668369dda4bb13524098aed9306ab45a0
+2015-04-23 09:16 +0000 [f70d21b2cf]  George Joseph <george.joseph at fairview5.com>
+
+	* res_pjsip:  Validate that contact uris start with sip: or sips:
+
+	  Currently we use pjsip_parse_hdr to validate contact uris but it
+	  appears that it allows uris without a scheme if there's a port
+	  supplied.  I.E myexample.com will fail but myexample.com:5060 will
+	  pass even though it has no scheme.  This causes SEGVs later on
+	  whenever the uri is used.
+
+	  To prevent this, permanent_contact_validate has been updated to check
+	  that the scheme is either 'sip' or 'sips'.
+
+	  2 uses of possibly-null endpoint have also been fixed in
+	  create_out_of_dialog_request.
+
+	  ASTERISK-24999
+
+	  Change-Id: Ifc17d16a4923e1045d37fe51e43bbe29fa556ca2
+	  Reported-by: Brad Latus
+
+2015-04-23 08:00 +0000 [1bb16bedc7]  Diederik de Groot <ddegroot at talon.nl>
+
+	* Clang: change previous tautological-compare fixes.
+
+	  clang can warn about a so called tautological-compare, when it finds
+	  comparisons which are logically always true, and are therefor deemed
+	  unnecessary.
+
+	  Exanple:
+	  unsigned int x = 4;
+	  if (x > 0)    // x is always going to be bigger than 0
+
+	  Enum Case:
+	  Each enumeration is its own type. Enums are an integer type but they
+	  do not have to be *signed*. C leaves it up to the compiler as an
+	  implementation option what to consider the integer type of a particu-
+	  lar enumeration is. Gcc treats an enum without negative values as
+	  an int while clang treats this enum as an unsigned int.
+
+	  rmudgett & mmichelson: cast the enum to (unsigned int) in assert.
+	  The cast does have an effect. For gcc, which seems to treat all enums
+	  as int, the cast to unsigned int will eliminate the possibility of
+	  negative values being allowed. For clang, which seems to treat enums
+	  without any negative members as unsigned int, the cast will have no
+	  effect. If for some reason in the future a negative value is ever
+	  added to the enum the assert will still catch the negative value.
+
+	  ASTERISK-24917
+
+	  Change-Id: I0557ae0154a0b7de68883848a609309cdf0aee6a
+
+2015-04-23 06:50 +0000 [a06924e9d9]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "Astobj2: Ensure all calls to __adjust_lock pass a valid object." into 13
+2015-04-22 16:22 +0000 [1474bb05f6]  George Joseph <george.joseph at fairview5.com>
+
+	* res_corosync: Add check for config file before calling corosync apis
+
+	  On some systems, res_corosync isn't compatible with the installed version of
+	  corosync so corosync_cfg_initialize fails, load_module returns LOAD_FAILURE,
+	  and Asterisk terminates.  The work around has been to remember to add
+	  res_corosync as a noload in modules.conf.  A better solution though is to have
+	  res_corosync check for its config file before attempting to call corosync apis
+	  and return LOAD_DECLINE if there's no config file.  This lets Asterisk loading
+	  continue.
+
+	  If you have a res_corosync.conf file and res_corosync fails, you get the same
+	  behavior as today and the fatal error tells you something is wrong with the
+	  install.
+
+	  ASTERISK-24998
+
+	  Change-Id: Iaf94a9431a4922ec4ec994003f02135acfdd3889
+2015-04-22 15:17 +0000 [73efb093b8]  Corey Farrell <git at cfware.com>
+
+	* Astobj2: Ensure all calls to __adjust_lock pass a valid object.
+
+	  __adjust_lock doesn't check for invalid objects, and doesn't have an
+	  appropriate return value for invalid objects.  Most callers of
+	  __adjust_lock pass objects that have already been confirmed valid,
+	  this change adds checks before the remaining calls.
+
+	  ASTERISK-24997 #close
+	  Reported by: Corey Farrell
+
+	  Change-Id: I669100f87937cc3f867cec56a27ae9c01292908f
+
+2015-04-22 16:32 +0000 [b0e929219b]  George Joseph <george.joseph at fairview5.com>
+
+	* .gitignore:  Add .gcno and .gcda
+
+	  Products of --enable-coverage
+
+	  Change-Id: Ie20882d64b60692e2c941ea8872ab82a86ce77a3
+
+2015-04-22 14:25 +0000 [5a3948a66f]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "Fix/Update clang-RAII macro implementation" into 13
+2015-04-22 14:07 +0000 [2ef1e1fc68]  Mark Michelson <mmichelson at digium.com>
+
+	* Merge "res_pjsip_mwi: Send unsolicited MWI NOTIFY on startup and when endpoint registers." into 13
+2015-04-22 04:17 +0000 [d6dfc85666]  Diederik de Groot <ddegroot at talon.nl>
+
+	* Clang: Fix some more tautological-compare warnings.
+
+	  clang can warn about a so called tautological-compare, when it finds
+	  comparisons which are logically always true, and are therefor deemed
+	  unnecessary.
+
+	  Exanple:
+	  unsigned int x = 4;
+	  if (x > 0)    // x is always going to be bigger than 0
+
+	  Enum Case:
+	  Each enumeration is its own type. Enums are an integer type but they
+	  do not have to be *signed*. C leaves it up to the compiler as an
+	  implementation option what to consider the integer type of a particu-
+	  lar enumeration is. Gcc treats an enum without negative values as
+	  an int while clang treats this enum as an unsigned int.
+
+	  rmudgett & mmichelson: cast the enum to (unsigned int) in assert.
+	  The cast does have an effect. For gcc, which seems to treat all enums
+	  as int, the cast to unsigned int will eliminate the possibility of
+	  negative values being allowed. For clang, which seems to treat enums
+	  without any negative members as unsigned int, the cast will have no
+	  effect. If for some reason in the future a negative value is ever
+	  added to the enum the assert will still catch the negative value.
+
+	  ASTERISK-24917
+	  Change-Id: Ief23ef68916192b9b72dabe702b543ecfeca0b62
+
+2015-04-22 05:45 +0000 [edd9e54818]  Joshua Colp <jcolp at digium.com>
+
+	* Merge "Check for ao2_alloc failure in __ast_channel_internal_alloc." into 13
+2015-04-14 14:04 +0000 [7b57116833]  Joshua Colp <jcolp at digium.com>
+
+	* res_pjsip_mwi: Send unsolicited MWI NOTIFY on startup and when endpoint registers.
+
+	  Currently the res_pjsip_mwi module only sends an unsolicited MWI NOTIFY upon
+	  a mailbox state change (such as a new message being left, or one being deleted).
+	  In practice this is not sufficient to keep clients aware of the current MWI status.
+
+	  This change makes the module send unsolicited MWI NOTIFY on startup so that
+	  clients are guaranteed to have the most up to date MWI information. It also makes
+	  clients receive an unsolicited MWI NOTIFY upon registration so if they are unaware
+	  of the current MWI status they receive it.
+
+	  ASTERISK-24982 #close
+	  Reported by: Joshua Colp
+
+	  Change-Id: I043f20230227e91218f18a82c7d5bb2aa62b1d58
+
+2015-04-22 05:29 +0000 [4423d5f755]  Joshua Colp <jcolp at digium.com>
+
+	* Merge "res_pjsip_pubsub: Set the endpoint on SUBSCRIBE dialogs." into 13
+2015-04-21 15:17 +0000 [ad1a118632]  Corey Farrell <git at cfware.com>
+
+	* Check for ao2_alloc failure in __ast_channel_internal_alloc.
+
+	  Fix a crash that could occur in __ast_channel_internal_alloc if
+	  ao2_alloc fails.
+
+	  ASTERISK-24991 #close
+
+	  Change-Id: I4ca89189eb22f907408cb87d0a1645cfe1314a90
+
+2015-04-20 14:30 +0000 [3327560cb2]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip_pubsub: Set the endpoint on SUBSCRIBE dialogs.
+
+	  When SUBSCRIBE dialogs were established, we never associated
+	  the endpoint that created the subscription with the dialog
+	  we end up creating. In most cases, this ended up not causing
+	  any problems.
+
+	  The actual bug that was observed was that when a device that
+	  was behind NAT established a subscription with Asterisk, Asterisk
+	  would end up sending in-dialog NOTIFY requests to the device's
+	  private IP addres instead of the public address of the NAT router.
+
+	  When Asterisk receives the initial SUBSCRIBE from the device,
+	  res_pjsip_nat rewrites the contact to the public address on which the
+	  SUBSCRIBE was received. This allows for the dialog to have its target
+	  address set to the proper public address. Asterisk then would send a 200
+	  OK response to the SUBSCRIBE, then a NOTIFY with the initial
+	  subscription state. The device would then send a 200 OK response to
+	  Asterisk's NOTIFY.
+
+	  Here's where things went wrong. When the 200 OK arrived, res_pjsip_nat
+	  did not rewrite the address in the Contact header. Then, when the PJSIP
+	  dialog layer processed the 200 OK, PJSIP would perform a comparison
+	  between the IP address in the Contact header and its saved target
+	  address for the dialog. Since they differed, PJSIP would update the
+	  target dialog address to be the address in the Contact header. From this
+	  point, if Asterisk needed to send a NOTIFY to the device, the result was
+	  that the NOTIFY would be sent to the private address that the device
+	  placed in the Contact header.
+
+	  The reason why res_pjsip_nat did not rewrite the address when it
+	  received the 200 OK response was that it could not associate the
+	  incoming response with a configured endpoint. This is because on a
+	  response, the only way to associate the response to an endpoint is by
+	  finding the dialog that the response is associated with and then finding
+	  the endpoint that is associated with that dialog. We do not perform
+	  endpoint lookups on responses. res_pjsip_pubsub skipped the step of
+	  associating the endpoint with the dialog we created, so res_pjsip_nat
+	  could not find the associated endpoint and therefore couldn't rewrite
+	  the contact.
+
+	  This commit message is like 50x longer than the actual fix.
+
+	  ASTERISK 24981 #close
+	  Reported by Mark Michelson
+
+	  Change-Id: I2b963c58c063bae293e038406f7d044a8a5377cd
+2015-04-20 18:00 +0000 [d08446ec36]  Richard Mudgett <rmudgett at digium.com>
+
+	* chan_dahdi/sig_pri: Make post AMI HangupRequest events on PRI channels.
+
+	  The chan_dahdi channel driver is a very old driver.  The ability for it to
+	  support ISDN was added well after the initial analog support.  Setting the
+	  softhangup flags is a carry over from the original analog code.  The
+	  driver was not updated to call ast_queue_hangup() which will post the AMI
+	  HangupRequest event.
+
+	  * Changed sig_pri.c to call ast_queue_hangup() instead of setting the
+	  softhangup flag when the remote party initiates a hangup.
+
+	  ASTERISK-24895 #close
+	  Reported by: Andrew Zherdin
+
+	  Change-Id: I5fe2e48556507785fd8ab8e1c960683fd5d20325
+
+2015-04-20 17:23 +0000 [96e18453f4]  Joshua Colp <jcolp at digium.com>
+
+	* Merge "pjsip_options:  Fix non-qualified contacts showing as unavailable" into 13
+2015-04-20 13:01 +0000 [2be9cc2643]  Diederik de Groot <ddegroot at talon.nl>
+
+	* Fix/Update clang-RAII macro implementation
+
+	  - When you need to refer to 'variable XXX' outside a block, it needs
+	  to be declared as '__block XXX', otherwise it will not be available with-
+	  in the block, making updating that variable hard to do, and ast_free
+	  lead to issues.
+
+	  - Removed the #error message
+	  because it creates complications when compiling external projects
+	  against asterisk For example when using a different compiler than the
+	  one used to compile asterisk. The warning/error should be generated
+	  during the configure process not the compilation process
+
+	  ASTERISK-24917
+	  Change-Id: I12091228090e90831bf2b498293858f46ea7a8c2
+2015-04-20 09:53 +0000 [b74b2cdcda]  George Joseph <george.joseph at fairview5.com>
+
+	* pjsip_options:  Fix format specifier for int64_t rtt.
+
+	  Contact status rtt is an int64_t and needs the PRId64 macro to
+	  properly create the format specifier on 32-bit systems.
+
+	  Change-Id: I4b8ab958fc1e9a179556a9b4ffa49673ba9fdec7
+
+2015-04-20 06:29 +0000 [27a122af66]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "main/pbx: Don't attempt to destroy a previously destroyed exten/priority tuple" into 13
+2015-04-20 05:54 +0000 [9581a0ebf3]  Joshua Colp <jcolp at digium.com>
+
+	* Merge "Fix issue with AST_THREADSTORAGE_RAW when DEBUG_THREADLOCALS is enabled." into 13
+2015-04-18 13:36 +0000 [63169e00ff]  George Joseph <george.joseph at fairview5.com>
+
+	* pjsip_options:  Fix non-qualified contacts showing as unavailable
+
+	  The "Add qualify_timeout processing and eventing" patch introduced
+	  an issue where contacts that had qualify_frequency set to 0 were
+	  showing Unavailable instead Unknown.  This patch checks for
+	  qualify_frequency=0 and create an "Unknown"  contact_status
+	  with an RTT = 0.
+
+	  Previously, the lack of contact_status implied Unknown but since
+	  we're now changing endpoint state based on contact_status, I've
+	  had to add new UNKNOWN status so that changes could trigger the
+	  appropriate contact_status observers.
+
+	  ASTERISK-24977: #close
+
+	  Change-Id: Ifcbc01533ce57f0e4e584b89a395326e098b8fe7
+
+2015-04-19 15:49 +0000 [f0c82a173a]  Matt Jordan <mjordan at digium.com>
+
+	* main/pbx: Don't attempt to destroy a previously destroyed exten/priority tuple
+
+	  When a PBX registrar is unloaded, it will fail to remove its extension from
+	  the context root_table if a dialplan application used by that extension is
+	  still loaded. This can be the case for AGI, which can be unloaded after several
+	  of the standard PBX providers. Often, this is harmless; however, if the
+	  extension's priorities are removed during the failed unloading *and* the
+	  dialplan application later unregisters, it leaves a ticking timebomb for the
+	  next PBX provider that attempts to iterate over the extensions. When that
+	  occurs, the peer_table pointer on the extension will already be set to NULL.
+	  The current code does not check to see if the pointer is NULL before passing
+	  it to a hashtab function this is not NULL tolerant.
+
+	  Since it is possible for the peer_table to be NULL when we normally would not
+	  expect that to be the case, the solution in this patch is to simply skip over
+	  processing an extension's priorities if peer_table is NULL.
+
+	  Prior to this patch, the tests/pbx/callerid_match test would crash during
+	  module unload. With this patch, the test no longer crashes after running.
+
+	  ASTERISK-24774 #close
+	  Reported by: Corey Farrell
+
+	  Change-Id: I2bbeecb7e0f77bac303a1b9135e4cdb4db6d4c40
+
+2015-04-17 18:05 +0000 [82bc0fd3ad]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_fax: Fix latent bug exposed by ASTERISK-24841 changes.
+
+	  Three fax related tests started failing as a result of changes made for
+	  ASTERISK-24841:
+	  tests/fax/pjsip/gateway_t38_g711
+	  tests/fax/sip/gateway_mix1
+	  tests/fax/sip/gateway_mix3
+
+	  Historically, ast_channel_make_compatible() did nothing if the channels
+	  were already "compatible" even if they had a sub-optimal translation path
+	  already setup.  With the changes from ASTERISK-24841 this is no longer
+	  true in order to allow the best translation paths to always be picked.  In
+	  res_fax.c:fax_gateway_framehook() code manually setup the channels to go
+	  through slin and then called ast_channel_make_compatible().  With the
+	  previous version of ast_channel_make_compatible() this was always a
+	  no-operation.
+
+	  * Remove call to ast_channel_make_compatible() in fax_gateway_framehook()
+	  that now undoes what was just setup when the framehook is attached.
+
+	  * Fixed locking around saving the channel formats in
+	  fax_gateway_framehook() to ensure that the formats that are saved are
+	  consistent.
+
+	  * Fix copy pasta errors in fax_gateway_framehook() that confuses read and
+	  write when dealing with saved channel formats.
+
+	  ASTERISK-24841
+	  Reported by: Matt Jordan
+
+	  Change-Id: I6fda0877104a370af586a5e8cf9e161a484da78d
+
+2015-04-17 16:19 +0000 [c59a800707]  Corey Farrell <git at cfware.com>
+
+	* Fix issue with AST_THREADSTORAGE_RAW when DEBUG_THREADLOCALS is enabled.
+
+	  When DEBUG_THREADLOCALS is enabled it causes the threadlocal cleanup to be
+	  called as a function.  This causes a compile error with raw threadstorage as
+	  it uses NULL for cleanup.  This fix uses a macro that provides NULL when
+	  DEBUG_THREADLOCALS is disabled, and replaces the call to "c_cleanup(data);"
+	  with "{};" when DEBUG_THREADLOCALS is enabled.
+
+	  ASTERISK-24975 #close
+	  Reported by: Ashley Sanders
+
+	  Change-Id: I3ef7428ee402816d9fcefa1b3b95830c00d5c402
+
+2015-04-17 15:57 +0000 [e05b076827]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "Detect potential forwarding loops based on count." into 13
+2015-04-15 10:38 +0000 [4f1a8dbe92]  Mark Michelson <mmichelson at digium.com>
+
+	* Detect potential forwarding loops based on count.
+
+	  A potential problem that can arise is the following:
+
+	  * Bob's phone is programmed to automatically forward to Carol.
+	  * Carol's phone is programmed to automatically forward to Bob.
+	  * Alice calls Bob.
+
+	  If left unchecked, this results in an endless loops of call forwards
+	  that would eventually result in some sort of fiery crash.
+
+	  Asterisk's method of solving this issue was to track which interfaces
+	  had been dialed. If a destination were dialed a second time, then
+	  the attempt to call that destination would fail since a loop was
+	  detected.
+
+	  The problem with this method is that call forwarding has evolved. Some
+	  SIP phones allow for a user to manually forward an incoming call to an
+	  ad-hoc destination. This can mean that:
+
+	  * There are legitimate use cases where a device may be dialed multiple
+	  times, or
+	  * There can be human error when forwarding calls.
+
+	  This change removes the old method of detecting forwarding loops in
+	  favor of keeping a count of the number of destinations a channel has
+	  dialed on a particular branch of a call. If the number exceeds the
+	  set number of max forwards, then the call fails. This approach has
+	  the following advantages over the old:
+
+	  * It is much simpler.
+	  * It can detect loops involving local channels.
+	  * It is user configurable.
+
+	  The only disadvantage it has is that in the case where there is a
+	  legitimate forwarding loop present, it takes longer to detect it.
+	  However, the forwarding loop is still properly detected and the
+	  call is cleaned up as it should be.
+
+	  Address review feedback on gerrit.
+
+	  * Correct "mfgium" to "Digium"
+	  * Decrement max forwards by one in the case where allocation of the
+	    max forwards datastore is required.
+	  * Remove irrelevant code change from pjsip_global_headers.c
+
+	  ASTERISK-24958 #close
+
+	  Change-Id: Ia7e4b7cd3bccfbd34d9a859838356931bba56c23
+2015-04-11 16:56 +0000 [674b18bdf0]  George Joseph <george.joseph at fairview5.com>
+
+	* pjsip_options: Add qualify_timeout processing and eventing
+
+	  This is the second follow-on to https://reviewboard.asterisk.org/r/4572/ and the
+	  discussion at
+	  http://lists.digium.com/pipermail/asterisk-dev/2015-March/073921.html
+
+	  The basic issues are that changes in contact status don't cause events to be
+	  emitted for the associated endpoint.  Only dynamic contact add/delete actions
+	  update the endpoint.  Also, the qualify timeout is fixed by pjsip at 32 seconds
+	  which is a long time.
+
+	  This patch makes use of the new transaction timeout feature in r4585 and
+	  provides the following capabilities...
+
+	  1.  A new aor/contact variable 'qualify_timeout' has been added that allows the
+	  user to specify the maximum time in milliseconds to wait for a response to an
+	  OPTIONS message.  The default is 3000ms.  When the timer expires, the contact is
+	  marked unavailable.
+
+	  2.  Contact status changes are now propagated up to the endpoint as follows...
+	  When any contact is 'Available', the endpoint is marked as 'Reachable'.  When
+	  all contacts are 'Unavailable', the endpoint is marked as 'Unreachable'.  The
+	  existing endpoint events are generated appropriately.
+
+	  ASTERISK-24863 #close
+
+	  Change-Id: Id0ce0528e58014da1324856ea537e7765466044a
+	  Tested-by: Dmitriy Serov
+	  Tested-by: George Joseph <george.joseph at fairview5.com>
+
+2015-04-17 15:29 +0000 [f1abf51b73]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "res_pjsip: Refactor endpt_send_request to include transaction timeout" into 13
+2015-04-17 10:30 +0000 [ab5b38e434]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "res_pjsip: Add global option to limit the maximum time for initial qualifies" into 13
+2015-04-17 10:25 +0000 [ec77b6148f]  Joshua Colp <jcolp at digium.com>
+
+	* Merge "res_pjsip_pubsub: On notify fail deleted sub_tree is then referenced" into 13
+2015-04-16 10:51 +0000 [b56c1914fa]  Kevin Harwell <kharwell at digium.com>
+
+	* bridge.c: NULL app causes crash during attended transfer
+
+	  Due to a race condition there was a chance that during an attended transfer the
+	  channel's application would return NULL. This, of course, would cause a crash
+	  when attempting to access the memory. This patch retrieves the channel's app
+	  at an earlier time in processing in hopes that the app name is available.
+	  However, if it is not then "unknown" is used instead. Since some string value
+	  is now always present the crash can no longer occur.
+
+	  ASTERISK-24869 #close
+	  Reported by: viniciusfontes
+	  Review:
+
+	  Change-Id: I5134b84c4524906d8148817719d76ffb306488ac
+
+2015-04-16 13:20 +0000 [8d4ce7cc2b]  Scott Griepentrog <scott at griepentrog.com>
+
+	* res_pjsip_pubsub: On notify fail deleted sub_tree is then referenced
+
+	  This change makes the send_notify of the sub_tree
+	  not happen when the sub_tree has been deleted due
+	  to the notify call failing, which avoids a crash.
+
+	  ASTERISK-24970 #close
+
+	  Change-Id: I1f20ffc08b192f59c457293b218025a693992cbf
+2015-04-11 16:39 +0000 [bf46799f0e]  George Joseph <george.joseph at fairview5.com>
+
+	* res_pjsip: Refactor endpt_send_request to include transaction timeout
+
+	  This is the first follow-on to https://reviewboard.asterisk.org/r/4572/ and the
+	  discussion at
+	  http://lists.digium.com/pipermail/asterisk-dev/2015-March/073921.html
+
+	  Since we currently have no control over pjproject transaction timeout, this
+	  patch pulls the pjsip_endpt_send_request function out of pjproject and into
+	  res_pjsip/endpt_send_transaction in order to implement that capability.
+
+	  Now when the transaction is initiated, we also schedule our own pj_timer with
+	  our own desired timeout.
+
+	  If the transaction completes before either timeout, pjproject cancels its timer,
+	  and calls our tsx callback where we cancel our timer and run the app callback.
+
+	  If the pjproject timer times out first, pjproject calls our tsx callback where
+	  we cancel our timer and run the app callback.
+
+	  If our timer times out first, we terminate the transaction which causes
+	  pjproject to cancel its timer and call our tsx callback where we run the app
+	  callback.
+
+	  Regardless of the scenario, pjproject is calling the tsx callback inside the
+	  group_lock and there are checks in the callback to make sure it doesn't run
+	  twice.
+
+	  As part of this patch ast_sip_send_out_of_dialog_request was created to replace
+	  its similarly named private function.  It takes a new timeout argument in
+	  milliseconds (<= 0 to disable the timeout).
+
+	  ASTERISK-24863 #close
+	  Reported-by: George Joseph <george.joseph at fairview5.com>
+	  Tested-by: George Joseph <george.joseph at fairview5.com>
+
+	  Change-Id: I0778dc730d9689c5147a444a04aee3c1026bf747
+2015-04-11 17:04 +0000 [1b6f6ff841]  George Joseph <george.joseph at fairview5.com>
+
+	* res_pjsip: Add global option to limit the maximum time for initial qualifies
+
+	  Currently when Asterisk starts initial qualifies of contacts are spread out
+	  randomly between 0 and qualify_timeout to prevent network and system overload.
+	  If a contact's qualify_frequency is 5 minutes however, that contact may be
+	  unavailable to accept calls for the entire 5 minutes after startup.  So while
+	  staggering the initial qualifies is a good idea, basing the time on
+	  qualify_timeout could leave contacts unavailable for too long.
+
+	  This patch adds a new global parameter "max_initial_qualify_time" that sets the
+	  maximum time for the initial qualifies.  This way you could make sure that all
+	  your contacts are initialy, randomly qualified within say 30 seconds but still
+	  have the contact's ongoing qualifies at a 5 minute interval.
+
+	  If max_initial_qualify_time is > 0, the formula is initial_interval =
+	  min(max_initial_interval, qualify_timeout * random().  If not set,
+	  qualify_timeout is used.
+
+	  The default is "0" (disabled).
+
+	  ASTERISK-24863 #close
+
+	  Change-Id: Ib80498aa1ea9923277bef51d6a9015c9c79740f4
+	  Tested-by: George Joseph <george.joseph at fairview5.com>
+
+2015-04-15 16:08 +0000 [5d218cde87]  George Joseph <george.joseph at fairview5.com>
+
+	* More .gitignore updates
+
+	  Added .pyc and .sha1 to the top-level .gitignore.
+
+	  Change-Id: I7dfc4f554d54d22947b38140d3305007503cc16a
+	  Tested-by: George Joseph <george.joseph at fairview5.com>
+
+2015-04-15 13:36 +0000 [97f83c4c53]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "Build System: Replace comment about setting menuselect defaults." into 13
+2015-04-14 13:16 +0000 [abd56db3e0]  Rodrigo Ramírez Norambuena <decipher.hk at gmail.com>
+
+	* cel_pgsql: Fix name string for log on unable allocate memory.
+
+	  The LOG_ERROR has reference to CDR instead of CEL  for LENGTHEN_BUF1 and
+	  LENGTHEN_BUF2.
+
+	  ASTERISK-24965 #close
+	  Reported by: Rodrigo Ramirez Norambuena
+
+	  Change-Id: Icc818697d7d66d34bfe3048cdd15ca2b06c89744
+2015-04-14 13:48 +0000 [222fbe1d9a]  Corey Farrell <git at cfware.com>
+
+	* Build System: Replace comment about setting menuselect defaults.
+
+	  The Makefile claims that you can set default menuselect options by creating
+	  ~/.asterisk.makeopts or /etc/asterisk.makeopts, but those files have never
+	  been respected in Asterisk 11 or 13.  This changes the comment to accurately
+	  reflect that these files are not automatically used by the build system.
+
+	  ASTERISK-13721 #close
+	  Reported by: pj
+
+	  Change-Id: Ibde804ff196283def49ccb9432fbf224a22586e2
+
+2015-04-12 09:08 +0000 [07e729cc7b]  Rodrigo Ramírez Norambuena <decipher.hk at gmail.com>
+
+	* cdr_pgsql: Fix CLI "cdr show pgsql status" command.
+
+	  The command always showed the usage information.
+
+	  * Fix the error in command validation for CLI_SHOWUSAGE.
+
+	  ASTERISK-24959 #close
+	  Reported by: Rodrigo Ramirez Norambuena
+
+	  Change-Id: I584f0936bb01001336a468a55c1d05d79fe795d5
+	  (cherry picked from commit 23a180cade51e84b9def65b05759c3cb9feba225)
+
+2015-04-13 19:06 +0000 [7d43d85bea]  George Joseph <george.joseph at fairview5.com>
+
+	* .gitignore updates for master/13
+
+	  Added products of ./bootstrap
+
+	  Added nmenuselect and gmenuselect to menuselect/
+
+	  Change-Id: Ied658463958bafc04a9aff9ebc28e40c116a6e35
+
+2015-04-13 14:41 +0000 [3d27c223a5]  David M. Lee <dlee at respoke.io>
+
+	* Fixing extconf compile
+
+	  During the mass code deletion for clang support, a stray backslash was
+	  left behind that was causing utils to fail to compile.
+
+	  Change-Id: I60e5fa58c9a5b248bde23aaada79ff663f87a2a1
+
+2015-04-13 12:03 +0000 [30045b4e67]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "build_tools/make_version: Update version parsing for Git migration" into 13
+2015-04-13 10:47 +0000 [88dbf6653e]  Joshua Colp <jcolp at digium.com>
+
+	* Merge "res_monitor: Add dependency on func_periodic_hook." into 13
+2015-04-13 09:54 +0000 [e996d8f728]  Matt Jordan <mjordan at digium.com>
+
+	* build_tools/make_version: Update version parsing for Git migration
+
+	  External systems - such as the Asterisk Test Suite - require knowledge of the
+	  upstream branch. Unfortunately, after moving to Git, the Asterisk version
+	  currently consists of only a 'GIT" prefix followed by an object blob,
+	  e.g., GIT-as08d7. This makes it difficult for such systems to know what
+	  features are available in a particular check out of Asterisk.
+
+	  This patch fixes this by hardcoding the branch in a variable in the
+	  make_version script. Since the mainline branches are not changed often -
+	  typically only once a year - this is a reasonable approach to solving
+	  the problem, and is more reliable than parsing the output of 'git branch
+	  -vv'. Branches that track off of an upstream primary branch will then get the
+	  benefit of knowing which mainline branch they are currently based off
+	  of.
+
+	  ASTERISK-24954 #close
+
+	  Change-Id: I8090d5d548b6d19e917157ed530b914b7eaf9799
+
+2015-04-12 12:59 +0000 [d1a6f1a9f9]  Matt Jordan <mjordan at digium.com>
+
+	* git migration: Remove support for file versions
+
+	  Git does not support the ability to replace a token with a version
+	  string during check-in. While it does have support for replacing a
+	  token on clone, this is somewhat sub-optimal: the token is replaced
+	  with the object hash, which is not particularly easy for human
+	  consumption. What's more, in practice, the source file version was often
+	  not terribly useful. Generally, when triaging bugs, the overall version
+	  of Asterisk is far more useful than an individual SVN version of a file.
+	  As a result, this patch removes Asterisk's support for showing source file
+	  versions.
+
+	  Specifically, it does the following:
+	  * main/asterisk:
+	    - Refactor the file_version structure to reflect that it no longer
+	      tracks a version field.
+	    - Alter the "core show file version" CLI command such that it always
+	      reports the version of Asterisk. The file version is no longer
+	      available.
+
+	  * main/manager: The Version key now always reports the Asterisk version.
+
+	  * UPGRADE: Add notes for:
+	    - Modification to the ModuleCheck AMI Action.
+	    - Modification of the "core show file version" CLI command.
+
+	  Change-Id: Ia932d3c64cd18a14a3c894109baa657ec0a85d28
+
+2015-04-13 06:19 +0000 [0e4b997cd7]  Corey Farrell <git at cfware.com>
+
+	* res_monitor: Add dependency on func_periodic_hook.
+
+	  OPTIONAL_API has conditionals to define AST_OPTIONAL_API and
+	  AST_OPTIONAL_API_ATTR differently based on if AST_API_MODULE is defined.
+	  Unfortunately this is inside the include protection block, so only the
+	  first status of AST_API_MODULE is respected.  For example res_monitor
+	  is an optional API provider, but uses func_periodic_hook.  This makes
+	  func_periodic_hook non-optional to res_monitor.
+
+	  ASTERISK-17608 #close
+	  Reported by: Warren Selby
+
+	  Change-Id: I8fcf2a5e7b481893e17484ecde4f172c9ffb5679
+
+2015-04-12 15:27 +0000 [91c1ed7ef6]  Matt Jordan <mjordan at digium.com>
+
+	* Merge "main/editline: Add .gitignore." into 13
+2015-04-12 06:12 +0000 [a77c31b99c]  Corey Farrell <git at cfware.com>
+
+	* main/editline: Add .gitignore.
+
+	  This patch adds a .gitignore for main/editline to ignore all build results.
+
+	  Change-Id: I68c7bf375ea46282689e5a706534b69fca233b5d
+
+2015-04-11 23:22 +0000 [d918c3b78e]  Matt Jordan <mjordan at digium.com>
+
+	* .gitignore: Ignore tarballs (*.gz)
+
+	  This patch updates the root .gitignore file to ignore files with a .gz
+	  extension. This will cause git to ignore downloaded sound tarballs in
+	  the the sounds/ directory.
+
+	  Change-Id: I1e42fbfa02a8884231507b683e8e49ac3e278aaa
+
+2015-04-11 13:20 +0000 [555b5f5d30]  George Joseph <george.joseph at fairview5.com>
+
+	* Add .gitignore and .gitreview files
+
+	  Add the .gitignore and .gitreview files to the asterisk repo.
+
+	  NB:  You can add local ignores to the .git/info/exclude file
+	  without having to do a commit.
+
+	  Common ignore patterns are in the top-level .gitignore file.
+	  Subdirectory-specific ignore patterns are in their own .gitignore
+	  files.
+
+	  Change-Id: I4c8af3b8e3739957db545f7368ac53f38e99f696
+	  Tested-by: George Joseph
+
+2015-04-11 10:35 +0000 [5807ca519c]  Matthew Jordan <mjordan at digium.com>
+
+	* Blocked revisions 434708
+
+	  ........
+	  main/event: Remove unnecessary assignment of negative value to enum
+
+	  When cleaning up some clang compiler warnings, the comparison of a negative
+	  value to an unsigned enum was removed. However, the initial assignment of a
+	  negative value to said enum remained in the variable declaration. This patch
+	  removes that assignment.
+
+	  Thanks to ibercom in #asterisk-bugs for pointing it out.
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434709 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-11 10:26 +0000 [d0d78d5732]  dkdegroot (License 6600)
+
+	* clang compiler warnings: Fix various warnings for tests
+
+	  This patch fixes a variety of clang compiler warnings for unit tests. This
+	  includes autological comparison issues, ignored return values, and
+	  interestingly enough, one embedded function. Fun!
+
+	  Review: https://reviewboard.asterisk.org/r/4555
+
+	  ASTERISK-24917
+	  Reported by: dkdegroot
+	  patches:
+	    rb4555.patch submitted by dkdegroot (License 6600)
+	  ........
+
+	  Merged revisions 434705 from http://svn.asterisk.org/svn/asterisk/branches/11
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434706 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-11 10:10 +0000 [4cf7d0bf01]  Juergen Spies (License 6698)
+
+	* res/res_pjsip_t38: Add missing initialization of t38faxmaxdatagram
+
+	  Prior to this patch, the far_max_datagram value on the UDPTL structure would
+	  remain -1 if the remote endpoint fails to provide the SDP media attribute
+	  T38FaxMaxDatagram. This can result in the INVITE request being rejected. With
+	  this patch, we will now properly initialize the value with either the default
+	  value or with the value provided by pjsip.conf's t38_udptl_maxdatagram
+	  parameter.
+
+	  Review: https://reviewboard.asterisk.org/r/4589
+
+	  ASTERISK-24928 #close
+	  Reported by: Juergen Spies
+	  Tested by: Juergen Spies
+	  patches:
+	    pjsipT38patch20150331.txt submitted by Juergen Spies (License 6698)
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434688 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-10 18:29 +0000 [13cd99682d]  Richard Mudgett <rmudgett at digium.com>
+
+	* chan_pjsip/res_pjsip/bridge_softmix/core: Improve translation path choices.
+
+	  With this patch, chan_pjsip/res_pjsip now sets the native formats to the
+	  codecs negotiated by a call.
+
+	  * The changes in chan_pjsip.c and res_pjsip_sdp_rtp.c set the native
+	  formats to include all the negotiated audio codecs instead of only the
+	  initial preferred audio codec and later the currently received audio
+	  codec.
+
+	  * The audio frame handling in channel.c:ast_read() is more streamlined and
+	  will automatically adjust to changes in received frame formats.  The new
+	  policy is to remove translation and pass the new frame format to the
+	  receiver except if the translation was to a signed linear format.  A more
+	  long winded version is commented in ast_read() along with some caveats.
+
+	  * The audio frame handling in channel.c:ast_write() is more streamlined
+	  and will automatically adjust any needed translation to changes in the
+	  frame formats sent.  Frame formats sent can change for many reasons such
+	  as a recording is being played back or the bridged peer changed the format
+	  it sends.  Since it is a normal expectation that sent formats can change,
+	  the codec mismatch warning message is demoted to a debug message.
+
+	  * Removed the short circuit check in
+	  channel.c:ast_channel_make_compatible_helper().  Two party bridges need to
+	  make channels compatible with each other.  However, transfers and moving
+	  channels among bridges can result in otherwise compatible channels having
+	  sub-optimal translation paths if the make compatible check is short
+	  circuited.  A result of forcing the reevaluation of channel compatibility
+	  is that the asterisk.conf:transcode_via_slin and codecs.conf:genericplc
+	  options take effect consistently now.  It is unfortunate that these two
+	  options are enabled by default and negate some of the benefits to the
+	  changes in channel.c:ast_read() by forcing translation through signed
+	  linear on a two party bridge.
+
+	  * Improved the softmix bridge technology to better control the translation
+	  of frames to the bridge.  All of the incoming translation is now normally
+	  handled by ast_read() instead of splitting any translation steps between
+	  ast_read() and the slin factory.  If any frame comes in with an unexpected
+	  format then the translation path in ast_read() is updated for the next
+	  frame and the slin factory handles the current frame translation.
+
+	  This is the final patch in a series of patches aimed at improving
+	  translation path choices.  The other patches are on the following reviews:
+	  https://reviewboard.asterisk.org/r/4600/
+	  https://reviewboard.asterisk.org/r/4605/
+
+	  ASTERISK-24841 #close
+	  Reported by: Matt Jordan
+
+	  Review: https://reviewboard.asterisk.org/r/4609/
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434671 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-10 16:03 +0000 [af458e2e60]  Kevin Harwell <kharwell at digium.com>
+
+	* chan_sip: make progressinband default to no
+
+	  After the "progressinband" value setting of "never" was updated to never send a
+	  183 this separated its use from the "no" value. Since "never" was the default,
+	  but most users probably expect "no" this patch updates the default for the
+	  "progressinband" setting to "no."
+
+	  ASTERISK-24835 #close
+	  Reported by: Andrew Nagy
+	  Review: https://reviewboard.asterisk.org/r/4606/
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434654 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-10 12:53 +0000 [88b0fa7755]  yaron nahum (License 6676)
+
+	* res_pjsip: Add an 'auto' option for DTMF Mode
+
+	  This patch adds support for automatically detecting the type of DTMF that a
+	  PJSIP endpoint supports. When the 'dtmf_mode' endpoint option is set to 'auto',
+	  the channel created for an endpoint will attempt to determine if RFC 4733
+	  DTMF is supported. If so, it will use that DTMF type. If not, the DTMF type
+	  for the channel will be set to inband.
+
+	  Review: https://reviewboard.asterisk.org/r/4438
+
+	  ASTERISK-24706 #close
+	  Reported by: yaron nahum
+	  patches:
+	    yaron_patch_3_Feb.diff submitted by yaron nahum (License 6676)
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434637 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-10 11:59 +0000 [16afee4651]  George Joseph <george.joseph at fairview5.com>
+
+	* res_pjsip_config_wizard: Cleanup load unload
+
+	  While investigating other unload issues I realized that the load/unload process 
+	  for the config wizard was pretty ugly so I've refactored it as follows...
+
+	  When the res_pjsip sorcery instance is created the config_wizard bumps it's own 
+	  module reference to prevent it from unloading while the sorcery instance is 
+	  still active.  When res_pjsip unloads and it's sorcery instance is destroyed, 
+	  the config wizard unrefs itself which then allows itself to unload cleanly.  
+	  Since the config wizard now can't load after res_pjsip or unload before it 
+	  (which should have been the correct behavior all along), I was able to remove 
+	  the chunks of code in both load_module and unload_module that handled that case.
+
+	  Ran the testsuite tests to insure there were no functional changes and REF_DEBUG 
+	  to insure that Asterisk was shutting down cleanly with no FRACKs or leaks.
+
+	  Tested-by: George Joseph
+	  Review: https://reviewboard.asterisk.org/r/4610/
+
+
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434619 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-10 11:37 +0000 [125acc52fe]  Richard Mudgett <rmudgett at digium.com>
+
+	* bridge_softmix.c,channel.c: Minor code simplification and cleanup.
+
+	  * Made code easier to follow in bridge_softmix.c:analyse_softmix_stats()
+	  and made some debug messages more helpful.
+
+	  * Made some debug and warning messages more helpful in
+	  channel.c:set_format().
+
+	  Review: https://reviewboard.asterisk.org/r/4607/
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434617 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-10 11:28 +0000 [a63f7ad04a]  Richard Mudgett <rmudgett at digium.com>
+
+	* translate.c: Only select audio codecs to determine the best translation choice.
+
+	  Given a source capability of h264 and ulaw, a destination capability of
+	  h264 and g722 then ast_translator_best_choice() would pick h264 as the
+	  best choice even though h264 is a video codec and Asterisk only supports
+	  translation of audio codecs.  When the audio starts flowing, there are
+	  warnings about a codec mismatch when the channel tries to write a frame to
+	  the peer.
+
+	  * Made ast_translator_best_choice() only select audio codecs.
+
+	  * Restore a check in channel.c:set_format() lost after v1.8 to prevent
+	  trying to set a non-audio codec.
+
+	  This is an intermediate patch for a series of patches aimed at improving
+	  translation path choices for ASTERISK-24841.
+
+	  This patch is a complete enough fix for ASTERISK-21777 as the v11 version
+	  of ast_translator_best_choice() does the same thing.  However, chan_sip.c
+	  still somehow tries to call ast_codec_choose() which then calls
+	  ast_best_codec() with a capability set that doesn't contain any audio
+	  formats for the incoming call.  The remaining warning message seems to be
+	  a benign transient.
+
+	  ASTERISK-21777 #close
+	  Reported by: Nick Ruggles
+
+	  ASTERISK-24380 #close
+	  Reported by: Matt Jordan
+
+	  Review: https://reviewboard.asterisk.org/r/4605/
+	  ........
+
+	  Merged revisions 434614 from http://svn.asterisk.org/svn/asterisk/branches/11
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434615 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-10 09:55 +0000 [c9791dba1f]  Matthew Jordan <mjordan at digium.com>
+
+	* res/ari: Fix model validation for ChannelHold event
+
+	  When the ChannelHold event was added, the 'musicclass' parameter was
+	  erroneously removed. This caused the ChannelHold events to be rejected as
+	  they failed model validation. This patch updates the Swagger schema such that
+	  it now properly reflects the event that is being created.
+
+	  Hooray for tests that catch things like this.
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434597 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-10 07:39 +0000 [c39faa4729]  Y Ateya (License 6693)
+
+	* channels/chan_iax2: Improve POKE expiration time calculation for lossy networks
+
+	  POKE is used to check for peer availability; however, in networks with packet
+	  loss, the current calculations may result in POKE expiration times that are too
+	  short. This patch alters the expiration/retry time logic to take into account
+	  the last known qualify round trip time, as opposed to always using a static
+	  value for each peer.
+
+	  Review: https://reviewboard.asterisk.org/r/4536
+
+	  ASTERISK-22352 #close
+	  Reported by: Frederic Van Espen
+
+	  ASTERISK-24894 #close
+	  Reported by: Y Ateya
+	  patches:
+	    poke_noanswer_duration.diff submitted by Y Ateya (License 6693)
+	  ........
+
+	  Merged revisions 434564 from http://svn.asterisk.org/svn/asterisk/branches/11
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434565 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-09 17:35 +0000 [75c2c85962]  George Joseph <george.joseph at fairview5.com>
+
+	* res_pjsip_phoneprov_provider: Fix reference leak on unload
+
+	  res_pjsip_phoneprov_provider was leaking references to phoneprov objects due to 
+	  a missing OBJ_NODATA in an ao2_callback in load_users().  Rather than adding the 
+	  OBJ_NODATA, I changed load_users to use a more straightforward ao2_iterator.  
+	  This plugged the leak but exposed an unload order issue between 
+	  res_pjsip_phoneprov_provider, res_phoneprov and res_pjsip.
+
+	  res_pjsip_phoneprov_provider unloads first, then res_phoneprov, then res_pjsip.  
+	  Since res_pjsip_phoneprov_provider uses res_pjsip's sorcery instance, when it 
+	  unloads, it's objects are still in the sorcery instance.  When res_pjsip 
+	  unloads, it destroys all its objects including res_pjsip_phoneprov_provider's.  
+	  The phoneprov destructor then attempts to unregister the extension from 
+	  res_phoneprov but because res_phoneprov is already cleaned up, its users 
+	  container is gone and we get a FRACK.
+
+	  Simple solution, check for the NULL users container before attempting to remove 
+	  the entry. Duh.
+
+	  Ran tests/res_phoneprov/res_phoneprov_provider.  No leaks in 
+	  res_pjsip_phoneprov_provider and no FRACKs.
+
+	  Reported-by: Corey Farrell
+	  Tested-by: George Joseph
+	  Review: https://reviewboard.asterisk.org/r/4608/
+	  ASTERISK-24935 #close
+
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434545 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-09 17:31 +0000 [73c286a393]  George Joseph <george.joseph at fairview5.com>
+
+	* loader/main: Don't set ast_fully_booted until deferred reloads are processed
+
+	  Until we have a true module management facility it's sometimes necessary for one 
+	  module to force a reload on another before its own load is complete.  If 
+	  Asterisk isn't fully booted yet, these reloads are deferred.  The problem is 
+	  that asterisk reports fully booted before processing the deferred reloads which 
+	  means Asterisk really isn't quite ready when it says it is.
+
+	  This patch moves the report of fully booted after the processing of the deferred 
+	  reloads is complete.
+
+	  Since the pjsip stack has the most number of related modules, I ran the 
+	  channels/pjsip testsuite to make sure there aren't any issues.  All tests 
+	  passed.
+
+	  Tested-by: George Joseph
+	  Review: https://reviewboard.asterisk.org/r/4604/
+
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434544 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-09 17:03 +0000 [5737650a67]  Kevin Harwell <kharwell at digium.com>
+
+	* res_pjsip: add CLI command to show global and system configuration
+
+	  Added a new CLI command for res_pjsip that shows both global and system
+	  configuration settings: pjsip show settings
+
+	  ASTERISK-24918 #close
+	  Reported by: Scott Griepentrog
+	  Review: https://reviewboard.asterisk.org/r/4597/
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434527 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-09 11:07 +0000 [1695a5b85f]  Richard Mudgett <rmudgett at digium.com>
+
+	* chan_iax2.c: Fix ref leak in iax2_request().
+
+	  * Increased warning message format capability string buffer size in
+	  iax2_request().
+
+	  Review: https://reviewboard.asterisk.org/r/4601/
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434510 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-09 10:54 +0000 [92c1688edb]  Richard Mudgett <rmudgett at digium.com>
+
+	* bridge_native_rtp.c: Defer allocation and check if it fails in native_rtp_bridge_compatible().
+
+	  Review: https://reviewboard.asterisk.org/r/4601/
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434508 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-09 10:42 +0000 [2679d0100a]  yaron nahum (License 6676)
+
+	* res/res_pjsip_dlg_options: Add a module to handle in-dialog OPTIONS requests
+
+	  This patch adds a new session supplement that handles in-dialog OPTIONS
+	  requests. Said OPTIONS requests are sent a 200 OK, as an endpoint lookup
+	  for the OPTIONS request would already have been done by the time the
+	  session supplement receives the inbound request.
+
+	  ASTERISK-24862 #close
+	  Reported by: yaron nahum
+	  patches:
+	    res_pjsip_dlg_options.c submitted by yaron nahum (License 6676)
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434506 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-09 07:56 +0000 [6ba6e3dffd]  dkdegroot (License 6600)
+
+	* clang compiler warnings: Fix autological comparisons
+
+	  This fixes autological comparison warnings in the following:
+	   * chan_skinny: letohl may return a signed or unsigned value, depending on the
+	     macro chosen
+	   * func_curl: Provide a specific cast to CURLoption to prevent mismatch
+	   * cel: Fix enum comparisons where the enum can never be negative
+	   * enum: Fix comparison of return result of dn_expand, which returns a signed
+	     int value
+	   * event: Fix enum comparisons where the enum can never be negative
+	   * indications: tone_data.freq1 and freq2 are unsigned, and hence can never be
+	     negative
+	   * presencestate: Use the actual enum value for INVALID state
+	   * security_events: Fix enum comparisons where the enum can never be negative
+	   * udptl: Don't bother to check if the return value from encode_length is less
+	     than 0, as it returns an unsigned int
+	   * translate: Since the parameters are unsigned int, don't bother checking
+	     to see if they are negative. The cast to unsigned int would already blow
+	     past the matrix bounds.
+	   * res_pjsip_exten_state: Use a temporary value to cache the return of
+	     ast_hint_presence_state
+	   * res_stasis_playback: Fix enum comparisons where the enum can never be
+	     negative
+	   * res_stasis_recording: Add an enum value for the case where the recording
+	     operation is in error; fix enum comparisons
+	   * resource_bridges: Use enum value as opposed to -1
+	   * resource_channels: Use enum value as opposed to -1
+
+	  Review: https://reviewboard.asterisk.org/r/4533
+	  ASTERISK-24917
+	  Reported by: dkdegroot
+	  patches:
+	    rb4533.patch submitted by dkdegroot (License 6600)
+	  ........
+
+	  Merged revisions 434469 from http://svn.asterisk.org/svn/asterisk/branches/11
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434470 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-08 21:05 +0000 [e05c8ae68e]  Stefan Engström (License 6691)
+
+	* apps/app_queue: Prevent possible crash when evaluating queue penalty rules
+
+	  Although it only occurred once, a crash occurred when a queue attempted to
+	  evaluate a queue penalty rule that appeared to have already been destroyed.
+	  In many locations in app_queue, a test is done to see if qe->pr is NULL;
+	  however, when we dispose of a queue's penalty rules, we don't set the pointer
+	  to NULL after free'ing it. This patch does that to prevent any dangling
+	  pointers from lingering on the queue object.
+
+	  Review: https://reviewboard.asterisk.org/r/4522
+
+	  ASTERISK-23319 #close
+	  Reported by: Vadim
+	  patches:
+	    rb4552.patch submitted by Stefan Engström (License 6691)
+	  ........
+
+	  Merged revisions 434448 from http://svn.asterisk.org/svn/asterisk/branches/11
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434449 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-08 13:15 +0000 [f21b45db49]  Jonathan Rose <jrose at digium.com>
+
+	* res_pjsip_t38: Fix FAX failures when using PJSIP with authentication
+
+	  Without this patch, if a PJSIP endpoint with udptl enabled and authentication
+	  set attempted to use sendFax, the FAX session would fail during setup. This
+	  was because the invite issued in response to being auth challenged would cause
+	  the PJSIP channel performing the FAX to receive a second T38 framehook and
+	  this would cause frames to be consumed in an inappropriate manner.
+
+	  ASTERISK-24933 #close
+	  Reported by: Jonathan Rose
+	  Review: https://reviewboard.asterisk.org/r/4577/
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434425 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-08 13:14 +0000 [4441bb6a25]  Richard Mudgett <rmudgett at digium.com>
+
+	* Bridging: Eliminate the unnecessary make channel compatible with bridge operation.
+
+	  When a channel enters the bridging system it is first made compatible with
+	  the bridge and then the bridge technology makes the channel compatible
+	  with the technology.  For all but the DAHDI native and softmix bridge
+	  technologies the make channel compatible with the bridge step is an
+	  effective noop because the other technologies allow all audio formats.
+	  For the DAHDI native bridge technology it doesn't matter because it is not
+	  an initial bridge technology and chan_dahdi allows only one native format
+	  per channel.  For the softmix bridge technology, it is a noop at best and
+	  harmful at worst because the wrong translation path could be setup if the
+	  channel's native formats allow more than one audio format.
+
+	  This is an intermediate patch for a series of patches aimed at improving
+	  translation path choices.
+
+	  * Removed code dealing with the unnecessary step of making the channel
+	  compatible with the bridge.
+
+	  ASTERISK-24841
+	  Reported by: Matt Jordan
+
+	  Review: https://reviewboard.asterisk.org/r/4600/
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434424 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-08 11:40 +0000 [f767440906]  mhej (license 6085)
+
+	* Security/tcptls: MitM Attack potential from certificate with NULL byte in CN.
+
+	  When registering to a SIP server with TLS, Asterisk will accept CA signed
+	  certificates with a common name that was signed for a domain other than the
+	  one requested if it contains a null character in the common name portion of
+	  the cert. This patch fixes that by checking that the common name length
+	  matches the the length of the content we actually read from the common name
+	  segment. Some certificate authorities automatically sign CA requests when
+	  the requesting CN isn't already taken, so an attacker could potentially
+	  register a CN with something like www.google.com\x00www.secretlyevil.net
+	  and have their certificate signed and Asterisk would accept that certificate
+	  as though it had been for www.google.com - this is a security fix and is
+	  noted in AST-2015-003.
+
+	  ASTERISK-24847 #close
+	  Reported by: Maciej Szmigiero
+	  Patches:
+	   asterisk-null-in-cn.patch submitted by mhej (license 6085)
+	  ........
+
+	  Merged revisions 434337 from http://svn.asterisk.org/svn/asterisk/branches/1.8
+	  ........
+
+	  Merged revisions 434338 from http://svn.asterisk.org/svn/asterisk/branches/11
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434384 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-08 11:23 +0000 [1712d16825]  Richard Mudgett <rmudgett at digium.com>
+
+	* format_cache.c: Add missing slin12 format to ast_format_cache_is_slinear().
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434357 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-08 07:33 +0000 [ae39dd1f46]  Matthew Jordan <mjordan at digium.com>
+
+	* chan_iax2: Fix compilation issue due to funky merge
+
+	  Don't mix declarations and code
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434314 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-08 07:00 +0000 [05397ad01e]  Jaco Kroon (License 5671)
+
+	* chan_iax2: Fix crash caused by unprotected access to iaxs[peer->callno]
+
+	  This patch fixes an access to the peer callnumber that is unprotected by a
+	  corresponding mutex. The peer->callno value can be changed by multiple threads,
+	  and all data inside the iaxs array must be procted by a corresponding lock
+	  of iaxsl.
+
+	  The patch moves the unprotected access to a location where the mutex is
+	  safely obtained.
+
+	  Review: https://reviewboard.asterisk.org/r/4599/
+
+	  ASTERISK-21211 #close
+	  Reported by: Jaco Kroon
+	  patches:
+	    asterisk-11.2.1-iax2_poke-segfault.diff submitted by Jaco Kroon (License 5671)
+	  ........
+
+	  Merged revisions 434291 from http://svn.asterisk.org/svn/asterisk/branches/11
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434292 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-08 06:53 +0000 [be13c72142]  Valentin Vidić (License 6697)
+
+	* chan_sip: Handle IPv4 mapped IPv6 clients when NAT is enabled
+
+	  When udpbindaddr is set to the IPv6 bind all address of '::', Asterisk will
+	  attempt to handle both IPv4 and IPv6 addresses, although the information will
+	  be stored in a struct with an AF_INET6 address type. However, the current
+	  NAT handling code won't handle the IPv4 mapped IPv6 addresses correctly.
+	  This patch adds an additional check for the mapped address case, allowing
+	  the NAT code to handle clients even when the address is IPv6.
+
+	  Review: https://reviewboard.asterisk.org/r/4563/
+
+	  ASTERISK-18032 #close
+	  Reported by: Christoph Timm
+	  patches:
+	    nat_with_ipv6.diff submitted by Valentin Vidić (License 6697)
+	  ........
+
+	  Merged revisions 434288 from http://svn.asterisk.org/svn/asterisk/branches/11
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434289 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-08 06:44 +0000 [f324870dab]  dkdegroot (License 6600)
+
+	* clang compiler warnings: Fix pointer-bool-converesion warnings
+
+	  This patch fixes several warnings pointed out by the clang compiler.
+	  * chan_pjsip: Removed check for data->text, as it will always be non-NULL.
+	  * app_minivm: Fixed evaluation of etemplate->locale, which will always
+	    evaluate to 'true'. This patch changes the evaluation to use
+	    ast_strlen_zero.
+	  * app_queue:
+	    - Fixed evaluation of qe->parent->monfmt, which always evaluates to
+	      true. Instead, we just check to see if the dereferenced pointer
+	      evaluates to true.
+	    - Fixed evaluation of mem->state_interface, wrapping it with a call to
+	      ast_strlen_zero.
+	  * res_smdi: Wrapped search_msg->mesg_desk_term with calls to ast_strlen_zero.
+
+	  Review: https://reviewboard.asterisk.org/r/4541
+
+	  ASTERISK-24917
+	  Reported by: dkdegroot
+	  patches:
+	    rb4541.patch submitted by dkdegroot (License 6600)
+	  ........
+
+	  Merged revisions 434285 from http://svn.asterisk.org/svn/asterisk/branches/11
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434286 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-07 14:38 +0000 [a6aed7f6f6]  Scott Griepentrog <sgriepentrog at digium.com>
+
+	* Revert accidental change in r434261
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434262 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-07 14:35 +0000 [0584e29300]  Scott Griepentrog <sgriepentrog at digium.com>
+
+	* pjsip: resolve compatibility problem with ast_sip_session
+
+	  A change in r430179 inserted a variable near the top of a
+	  structure caused a problem when running DPMA in a version
+	  of Asterisk compiled across the change.  This patch moves
+	  the new variable to the end of the structure, eliminating
+	  the problem.
+
+	  Review: https://reviewboard.asterisk.org/r/4574/
+	  ........
+
+	  Merged revisions 433944 from http://svn.asterisk.org/svn/asterisk/branches/13
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434261 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-07 11:40 +0000 [d754f70239]  Kevin Harwell <kharwell at digium.com>
+
+	* bridge.c: Hangup attended transfer target after it has been swapped out
+
+	  After completing an attended transfer the transfer target channel (the one that
+	  gets swapped out) was not being hung up after leaving the bridge. This resulted
+	  in a channel possibly being left around. Added an explicit softhangup for the
+	  channel in question after the transfer is successfully completed in order to
+	  make sure the channel is hung up.
+
+	  ASTERISK-24782 #close
+	  Reported by: John Bigelow
+	  Review: https://reviewboard.asterisk.org/r/4575/
+
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434240 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-07 10:33 +0000 [c516981dc7]  Mark Michelson <mmichelson at digium.com>
+
+	* Do not queue message requests that we do not respond to.
+
+	  If we receive a MESSAGE request that we cannot send a response
+	  to, we should not send the incoming MESSAGE to the dialplan.
+
+	  This commit should help the bouncing message_retrans test to
+	  pass consistently.
+
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434218 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-07 10:21 +0000 [ab803ec342]  Matthew Jordan <mjordan at digium.com>
+
+	* ARI: Add the ability to intercept hold and raise an event
+
+	  For some applications - such as SLA - a phone pressing hold should not behave
+	  in the fashion that the Asterisk core would like it to. Instead, the hold
+	  action has some application specific behaviour associated with it - such as
+	  disconnecting the channel that initiated the hold; only playing MoH to channels
+	  in the bridge if the channels are of a particular type, etc.
+
+	  One way of accomplishing this is to use a framehook to intercept the
+	  hold/unhold frames, raise an event, and eat the frame. Tasty. This patch
+	  accomplishes that using a new dialplan function, HOLD_INTERCEPT.
+
+	  In addition, some general cleanup of raising hold/unhold Stasis messages was
+	  done, including removing some RAII_VAR usage.
+
+	  Review: https://reviewboard.asterisk.org/r/4549/
+
+	  ASTERISK-24922 #close
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434216 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-06 21:09 +0000 [488f093e97]  dkdegroot (License 6600)
+
+	* clang compiler warnings: Fix sometimes-initialized warning in func_math
+
+	  This patch fixes a bug in a unit test in func_math where a variable could be
+	  passed to ast_free that wasn't allocated. This patch corrects the issue and
+	  ensures that we only attempt to free a variable if we previously allocated
+	  it.
+
+	  Review: https://reviewboard.asterisk.org/r/4552
+
+	  ASTERISK-24917
+	  Reported by: dkdegroot
+	  patches:
+	    rb4552.patch submitted by dkdegroot (License 6600)
+	  ........
+
+	  Merged revisions 434190 from http://svn.asterisk.org/svn/asterisk/branches/11
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434191 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-06 21:03 +0000 [c027133f6d]  dkdegroot (License 6600)
+
+	* clang compiler warnings: Fix non-literal-null-conversion warnings
+
+	  Clang will flag errors when a char pointer is set to '\0', as opposed to a
+	  value that the char pointer points to. This patch fixes this warning
+	  in a variety of locations.
+
+	  Review: https://reviewboard.asterisk.org/r/4551
+
+	  ASTERISK-24917
+	  Reported by: dkdegroot
+	  patches:
+	    rb4551.patch submitted by dkdegroot (License 6600)
+	  ........
+
+	  Merged revisions 434187 from http://svn.asterisk.org/svn/asterisk/branches/11
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434188 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-06 14:23 +0000 [2270c40d33]  Kevin Harwell <kharwell at digium.com>
+
+	* res_pjsip: config option 'timers' can't be set to 'no'
+
+	  When setting the configuration option 'timers' equal to 'no' the bit flag was
+	  not properly negated. This patch clears all associated flags and only sets the
+	  specified one. pjsip will handle any necessary flag combinations. Also went
+	  ahead and did similar for the '100rel' option.
+
+	  ASTERISK-24910 #close
+	  Reported by: Ray Crumrine
+	  Review: https://reviewboard.asterisk.org/r/4582/
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434131 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-06 14:02 +0000 [95de71f247]  George Joseph <george.joseph at fairview5.com>
+
+	* build: Fixes for gcc 5 compilation
+
+	  These are fixes for compilation under gcc 5.0...
+
+	  chan_sip.c:    In parse_request needed to make 'lim' unsigned.
+	  inline_api.h:  Needed to add a check for '__GNUC_STDC_INLINE__' to detect C99 
+	                 inline semantics (same as clang).
+	  ccss.c:        In ast_cc_set_parm, needed to fix weird comparison.
+	  dsp.c:         Needed to work around a possible compiler bug.  It was throwing 
+	                 an array-bounds error but neither
+	                 sgriepentrog, rmudgett nor I could figure out why.
+	  manager.c:     In action_atxfer, needed to correct an array allocation.
+
+	  This patch will go to 11, 13, trunk.
+
+	  Review: https://reviewboard.asterisk.org/r/4581/
+	  Reported-by: Jeffrey Ollie
+	  Tested-by: George Joseph
+	  ASTERISK-24932 #close
+	  ........
+
+	  Merged revisions 434113 from http://svn.asterisk.org/svn/asterisk/branches/11
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434114 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-06 13:18 +0000 [d54ccda3b1]  dkdegroot (License 6600)
+
+	* clang compiler warnings: Remove large chunks of unused code from extconf
+
+	  This patch fixes a warning caught by clang, in which it detected that large
+	  chunks of extconf were unused. Frankly, I wish we could pretend that all of
+	  extconf was unused, but alas, that is not yet the case.
+
+	  A few extraneous functions in the parking tests were removed as well, for
+	  the same reason.
+
+	  Review: https://reviewboard.asterisk.org/r/4553
+
+	  ASTERISK-24917
+	  Reported by: dkdegroot
+	  patches:
+	    rb4553.patch submitted by dkdegroot (License 6600)
+	  ........
+
+	  Merged revisions 434093 from http://svn.asterisk.org/svn/asterisk/branches/11
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434097 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-06 13:03 +0000 [0ecd472e4f]  dkdegroot (License 6600)
+
+	* clang compiler warnings: Fix sometimes-uninitialized warning in pbx_config
+
+	  This patch fixes a warning caught by clang, in which a char pointer could be
+	  assigned to before it was initialized. The patch re-organizes the code to
+	  ensure that the pointer is always initialized, even on off nominal paths.
+
+	  Review: https://reviewboard.asterisk.org/r/4529
+
+	  ASTERISK-24917
+	  Reported by: dkdegroot
+	  patches:
+	    rb4529.patch submitted by dkdegroot (License 6600)
+	  ........
+
+	  Merged revisions 434090 from http://svn.asterisk.org/svn/asterisk/branches/11
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434091 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-06 12:52 +0000 [4e7be5b2dc]  dkdegroot (License 6600)
+
+	* clang compiler warnings: Fix format specified in framehook
+
+	  This patch fixes an invalid format specifier used in the formatting of an
+	  ERROR message in the framehook code. The format specifier specifies a
+	  type of 'unsigned short', but the argument passed to it is of type 'int'.
+	  The patch changes the format specifier to 'i'.
+
+	  Review: https://reviewboard.asterisk.org/r/4540
+
+	  ASTERISK-24917
+	  Reported by: dkdegroot
+	  patches:
+	    rb4535.patch submitted by dkdegroot (License 6600)
+	  ........
+
+	  Merged revisions 434087 from http://svn.asterisk.org/svn/asterisk/branches/11
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434088 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-06 11:02 +0000 [2443b40341]  Mark Michelson <mmichelson at digium.com>
+
+	* Ensure that a non-zero sample rate is returned for all formats.
+
+	  Versions of Asterisk prior to 12 defaulted to 8000 as a sample rate
+	  if one was not provided by a format. In Asterisk 13, this was removed.
+	  The result was that some calculations which involve dividing by the
+	  sample rate resulted in dividing by 0. The fix being put in place
+	  here is to have the same default fallback that was present in previous
+	  versions of Asterisk.
+
+	  Asterisk-24914 #close
+	  Reported by Marcello Ceschia
+
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434046 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-06 10:16 +0000 [b1102cd642]  Corey Farrell <git at cfware.com>
+
+	* res_pjsip_phoneprov_provider: Revert 433996 / 433997.
+
+	  res_pjsip_phoneprov_provider is using ao2_callback with OBJ_MULTIPLE, then
+	  ignoring the return.  OBJ_NODATA flag was to prevent a reference leak, but
+	  this caused the module to FRACK on unload.  Revert change until this can
+	  be investigated further.
+
+	  ASTERISK-24935
+	  Reported by: Corey Farrell
+	  Review: https://reviewboard.asterisk.org/r/4578/
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434025 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-06 09:50 +0000 [0f25076f67]  Mark Michelson (license #5049)
+
+	* ParkedCall: Don't allow dialplan fallthrough after retrieving parked call.
+
+	  This is a change to align behavior with that of Asterisk 11 and previous versions.
+	  In those versions, if a parked call were retrieved, and the call ended, the parked
+	  call retriever would be hung up after the ParkedCall application ran. Prior to this
+	  patch, in Asterisk 13, the same situation would result in the parked call retriever
+	  falling through to additional priorities in the extension where the ParkedCall
+	  application was called. With this patch, the behavior between Asterisk 11 and 13
+	  aligns.
+
+	  ASTERISK-24899 #close
+	  Reported by Malcolm Davenport
+	  Patches:
+	  	ASTERISK-24899.patch uploaded by Mark Michelson(license #5049)
+
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@434022 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-05 07:53 +0000 [709fa14b44]  Corey Farrell <git at cfware.com>
+
+	* res_pjsip_phoneprov_provider: Fix leaked OBJ_MULTIPLE iterator.
+
+	  res_pjsip_phoneprov_provider was using ao2_callback with OBJ_MULTIPLE, then
+	  ignoring the return.  Added OBJ_NODATA flag to prevent a reference leak.
+
+	  ASTERISK-24935 #close
+	  Reported by: Corey Farrell
+	  Review: https://reviewboard.asterisk.org/r/4578/
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@433996 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-03 16:53 +0000 [1ee8424f27]  Mark Michelson <mmichelson at digium.com>
+
+	* res_pjsip_messaging: Serialize outbound SIP MESSAGEs
+
+	  Outbound SIP MESSAGEs had the potential to be sent out
+	  of order from how they were specified in a set of
+	  dialplan steps.
+
+	  This change creates a serializer for sending outbound
+	  MESSAGE requests on. This ensures that the MESSAGEs are
+	  sent by Asterisk in the same order that they were sent
+	  from the dialplan.
+
+	  ASTERISK-24937 #close
+	  Reported by Mark Michelson
+
+	  Review: https://reviewboard.asterisk.org/r/4579
+
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@433968 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-02 09:56 +0000 [169e57d2e0]  Scott Griepentrog <sgriepentrog at digium.com>
+
+	* pjsip: resolve compatibility problem with ast_sip_session
+
+	  A change in r430179 inserted a variable near the top of a
+	  structure caused a problem when running DPMA in a version
+	  of Asterisk compiled across the change.  This patch moves
+	  the new variable to the end of the structure, eliminating
+	  the problem.
+
+	  Review: https://reviewboard.asterisk.org/r/4574/
+
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@433944 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-02 05:31 +0000 [1eb0c5f4e8]  Corey Farrell <git at cfware.com>
+
+	* Tell menuselect that MALLOC_DEBUG conflicts with DEBUG_CHAOS.
+
+	  DEBUG_CHAOS was marked as conflicting with MALLOC_DEBUG, but
+	  for this to work correctly MALLOC_DEBUG must also be marked
+	  as conflicting with DEBUG_CHAOS.
+
+	  Review: https://reviewboard.asterisk.org/r/4557/
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@433923 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-04-01 11:25 +0000 [e301185983]  Ashley Sanders <asanders at digium.com>
+
+	* stasis: set a channel variable on websocket disconnect error
+
+	  Resolve compile errors caused by r433863 by fixing the
+	  documentation xml to comply with the schema.
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@433888 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-03-31 22:26 +0000 [a1f12d9231]  Ashley Sanders <asanders at digium.com>
+
+	* stasis: set a channel variable on websocket disconnect error
+
+	  Resolve compile errors caused by r433839 by included the missing
+	  header file, pbx.h.
+
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@433863 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-03-31 17:00 +0000 [7293ecd90b]  Ashley Sanders <asanders at digium.com>
+
+	* stasis: set a channel variable on websocket disconnect error
+
+	  When an error occurs while writing to a web socket, the web socket is
+	  disconnected and the event is logged. A side-effect of this, however, is that
+	  any application on the other side waiting for a response from Stasis is left
+	  hanging indefinitely (as there is no mechanism presently available for
+	  notifying interested parties about web socket error states in Stasis).
+
+	  To remedy this scenario, this patch introduces a new channel variable:
+	  STASISSTATUS.
+
+	  The possible values for STASISSTATUS are:
+	  SUCCESS         - The channel has exited Stasis without any failures
+	  FAILED          - Something caused Stasis to croak. Some (not all) possible
+	                    reasons for this:
+	                      - The app registry is not instantiated;
+	                      - The app requested is not registered;
+	                      - The app requested is not active;
+	                      - Stasis couldn't send a start message
+
+	  ASTERISK-24802
+	  Reported By: Kevin Harwell
+	  Review: https://reviewboard.asterisk.org/r/4519/
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@433839 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-03-31 11:55 +0000 [94949e7f2f]  Richard Mudgett <rmudgett at digium.com>
+
+	* chan_sip: Fix expression in unit test /channels/chan_sip/test_sip_rtpqos.
+
+	  Fix misplaced parentheses in original fabs() expression.
+	  ........
+
+	  Merged revisions 433816 from http://svn.asterisk.org/svn/asterisk/branches/11
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@433817 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-03-31 06:47 +0000 [9967739669]  Corey Farrell <git at cfware.com>
+
+	* Re-add _ast_mem_backtrace_buffer variable for ABI compatibility.
+
+	  Modules built prior to commit of r4502 expect to link at runtime
+	  to the variable _ast_mem_backtrace_buffer.  This change re-adds
+	  the variable to the C file only.
+
+	  Review: https://reviewboard.asterisk.org/r/4558/
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@433795 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-03-30 06:42 +0000 [2d39bc5528]  Corey Farrell <git at cfware.com>
+
+	* Fix an ABI compatibility issue with ast_log_safe for modules.
+
+	  Binary modules are sometimes built against the latest release of
+	  Asterisk in each branch, and need to be compatible with all
+	  releases of that branch.  This change ensures that utils.h only
+	  uses ast_log_safe from the core.  For modules and utilities ast_log
+	  is used instead.
+
+	  Review: https://reviewboard.asterisk.org/r/4548/
+	  ........
+
+	  Merged revisions 433772 from http://svn.asterisk.org/svn/asterisk/branches/11
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@433773 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-03-29 21:44 +0000 [5f8faf16af]  dkdegroot (License 6600)
+
+	* clang compiler warnings: Fix -Wabsolute-value warnings
+
+	  This patch fixes several warnings caught by clang - in this case, usage of the
+	  abs function on non-integer values. This patch uses labs and fabs, as
+	  appropriate, in the various affected files.
+
+	  Review: https://reviewboard.asterisk.org/r/4525
+
+	  ASTERISK-24917
+	  Reported by: dkdegroot
+	  patches:
+	    rb4525.patch submitted by dkdegroot (License 6600)
+	  ........
+
+	  Merged revisions 433749 from http://svn.asterisk.org/svn/asterisk/branches/11
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@433750 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-03-29 21:39 +0000 [09b681e344]  dkdegroot (License 6600)
+
+	* clang compiler warnings: Fix invalid enum conversion
+
+	  This patch fixes some invalid enum conversion warnings caught by clang. In
+	  particular:
+	  * chan_sip: Several functions mixed usage of the st_refresher_param
+	    enum and st_refresher enum. This patch corrects the functions to use the
+	    right enum.
+	  * chan_pjsip: Fixed mixed usage of ast_sip_session_t38state and ast_t38_state.
+	  * strings: Fixed incorrect usage of AO2 flags with strings container.
+	  * res_stasis: Change a return enumeration to stasis_app_user_event_res.
+
+	  Review: https://reviewboard.asterisk.org/r/4535
+
+	  ASTERISK-24917
+	  Reported by: dkdegroot
+	  patches:
+	    rb4535.patch submitted by dkdegroot (License 6600)
+	  ........
+
+	  Merged revisions 433746 from http://svn.asterisk.org/svn/asterisk/branches/11
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@433747 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-03-29 21:29 +0000 [7f33abb827]  Matthew Jordan <mjordan at digium.com>
+
+	* main/stdtime/localtime: Fix warning introduced in r433720
+
+	  The patch in r433720 caused a warning to be kicked back by gcc. It occurred
+	  due to this check in unistd.h:
+
+	      if (__nbytes > __bos0 (__buf))
+	          return __read_chk_warn (__fd, __buf, __nbytes, __bos0 (__buf));
+
+	  That is, if __nbytes is greater than the result of GCC's built-in object size
+	  for the struct, we'll kick back a warning.
+
+	  As it turns out, this is because there is an error in the code in the patch.
+	  We are passing the address of the pointer to the struct, not iev, which is a
+	  pointer to the struct. Hence, the number of bytes is probably going to be lot
+	  larger than the number of bytes that make up a pointer! This patch changes
+	  the code just read from the pointer to the struct - which fixes the warning.
+
+	  ASTERISK-24917
+	  ........
+
+	  Merged revisions 433743 from http://svn.asterisk.org/svn/asterisk/branches/11
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@433744 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-03-29 20:56 +0000 [47eeb67e14]  dkdegroot (License 6600)
+
+	* clang compiler warnings: Ignore -Wunused-command-line-argument
+
+	  Asterisk's build system has a tendency to pass include directives for libraries
+	  to everything compiled within a particular group of source files. This means
+	  we pass the header for libxml2 to things that don't necessarily need it. As a
+	  result, we ignore this particular warning.
+
+	  Review: https://reviewboard.asterisk.org/r/4545/
+
+	  ASTERISK-24917
+	  Reported by: dkdegroot
+	  patches:
+	    rb4545.patch submitted by dkdegroot (License 6600)
+	  ........
+
+	  Merged revisions 433720 from http://svn.asterisk.org/svn/asterisk/branches/11
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@433721 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-03-29 20:52 +0000 [dbb4d6f9e7]  dkdegroot (License 6600)
+
+	* clang compiler warnings: Fix warning for -Wgnu-variable-sized-type-not-at-end
+
+	  This patch fixes a warning caught by clang, wherein a variable sized struct is
+	  not located at the end of a struct. While the code in question actually
+	  expected this, this is a good warning to watch for. Hence, this patch refactors
+	  the code in question to not have two variable length elements in the same
+	  struct.
+
+	  Review: https://reviewboard.asterisk.org/r/4530/
+
+	  ASTERISK-24917
+	  Reported by: dkdegroot
+	  patches:
+	    rb4530.patch submitted by dkdegroot (License 6600)
+	  ........
+
+	  Merged revisions 433717 from http://svn.asterisk.org/svn/asterisk/branches/11
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@433718 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-03-28 07:56 +0000 [e126ab9eeb]  dkdegroot (License 6600)
+
+	* clang compiler warnings: Fix a variety of "unused" warnings
+
+	  This patch fixes the -Wunused-value -Wunused-variable -Wunused-const-variable
+	  errors caught by clang. Specifically:
+
+	  * apps/app_queue.c: removed unused qpm_cmd_usage[], qum_cmd_usage[],
+	                      qsmp_cmd_usage[]
+	  * cel/cel_sqlite3_custom.c: removed unused name[] = "cel_sqlite3_custom"
+	  * channels/chan_pjsip.c: removed unused desc[] = "PJSIP Channel"
+	  * codecs/gsm/src/gsm_create.c: removed unused ident[] = "$Header$"
+	  * funcs/func_env.c:729: Fixed ast_str_append_substr.
+	  * main/editline/np/strlcat.c: removed unused rcsid variable
+	  * main/editline/np/strlcpy.c: removed unused rcsid variable
+	  * main/security_events.c: removed unused TIMESTAMP_STR_LEN
+	  * utils/conf2ael.c: removed unused cfextension_states
+	  * utils/extconf.c: removed unused cfextension_states
+
+	  Review: https://reviewboard.asterisk.org/r/4526
+
+	  ASTERISK-24917
+	  Reported by: dkdegroot
+	  patches:
+	    rb4526.patch submitted by dkdegroot (License 6600)
+	  ........
+
+	  Merged revisions 433693 from http://svn.asterisk.org/svn/asterisk/branches/11
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@433694 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-03-28 07:48 +0000 [2f6534527d]  dkdegroot (License 6600)
+
+	* clang compiler warnings: Fix -Wself-assign
+
+	  Assigning a variable to itself isn't super useful. However, the WAV format
+	  modules make use of this in order to perform byte endian checks. This patch
+	  works around the warning by only performing the self assignment if we are
+	  going to do more than just assign it to ourselves. Which is odd, but true.
+
+	  Review: https://reviewboard.asterisk.org/r/4544/
+
+	  ASTERISK-24917
+	  Reported by: dkdegroot
+	  patches:
+	    rb4544.patch submitted by dkdegroot (License 6600)
+	  ........
+
+	  Merged revisions 433690 from http://svn.asterisk.org/svn/asterisk/branches/11
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@433691 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-03-28 07:40 +0000 [eb70993a50]  dkdegroot (License 6600)
+
+	* clang compiler warnings: Fix -Wparantheses-equality warnings
+
+	  Clang will treat ((a == b)) as a warning, as it reasonably expects that the
+	  developer may have intended to write (a == b) or ((a = b)). This patch cleans
+	  up all instances where equality, not assignment, was intended between two
+	  parantheses.
+
+	  Review: https://reviewboard.asterisk.org/r/4531/
+
+	  ASTERISK-24917
+	  Repoted by: dkdegroot
+	  patches:
+	    rb4531.patch submitted by dkdegroot (License 6600)
+	  ........
+
+	  Merged revisions 433687 from http://svn.asterisk.org/svn/asterisk/branches/11
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@433688 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-03-28 07:31 +0000 [c0ff16036a]  dkdegroot (License 6600)
+
+	* clang compiler warnings: Fix -Wbitfield-constant-conversion warning
+
+	  In chan_iax2, we attempt to assign a -1 to a bitfield. This gets caught by
+	  clang, as it will truncate the -1 to a 1 implicitly.
+
+	  Instead, we just assign the value a '1'.
+
+	  Review: https://reviewboard.asterisk.org/r/4537/
+
+	  ASTERISK-24917
+	  Reported by: dkdegroot
+	  patches:
+	    rb4537.patch submitted by dkdegroot (License 6600)
+	  ........
+
+	  Merged revisions 433683 from http://svn.asterisk.org/svn/asterisk/branches/11
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@433684 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-03-28 07:27 +0000 [844bc76bef]  dkdegroot (License 6600)
+
+	* clang compiler warnings: Fix -Winitializer-overrides
+
+	  This patch fixes clange compiler warnings for initializer overrides.
+	  Specifically:
+
+	  res_pjsip/config_transport maps PJSIP_TLSV1_METHOD to the same enumeration
+	  value as PJSIP_SSL_DEFAULT_METHOD. When initializing an array containing
+	  those enum values, we therefore initialize the value twice to two different
+	  values, "tlsv1" and "default". This patch changes it to just initialize
+	  the index in the array to "tlsv1".
+
+	  Review: https://reviewboard.asterisk.org/r/4539/
+
+	  ASTERISK-24917
+	  Reported by: dkdegroot
+	  patches:
+	    rb4539.patch submitted by dkdegroot (License 6600)
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@433682 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-03-28 07:19 +0000 [5e204042d9]  dkdegroot (License 6600)
+
+	* clang compiler warnings: Fix -Wunused-function; make inline function static
+
+	  This patch fixes clang compilers warnings for unused functions. Specifically:
+	   * channels/chan_iax2: removed user_ref function
+	   * main/dsp.c: removed goertzel_update function
+	   * main/config.c: made variable_list_switch static
+
+	  Review: https://reviewboard.asterisk.org/r/4527
+
+	  ASTERISK-24917
+	  Reported by: dkdegroot
+	  patches:
+	    rb4527.patch submitted by dkdegroot (License 6600)
+	  ........
+
+	  Merged revisions 433678 from http://svn.asterisk.org/svn/asterisk/branches/11
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@433680 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-03-27 17:34 +0000 [cfbf5fbe91]  Jonathan Rose <jrose at digium.com>
+
+	* SAC: Add a few basic queues
+
+	  Review: https://reviewboard.asterisk.org/r/4503/
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@433658 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-03-27 17:25 +0000 [1a50d8d4c2]  Jonathan Rose <jrose at digium.com>
+
+	* SAC: Add conferencing extensions and configuration
+
+	  Review: https://reviewboard.asterisk.org/r/4504/
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@433656 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-03-27 16:15 +0000 [c6c08d755d]  Rusty Newton <rnewton at digium.com>
+
+	* configs/basic-pbx - Super Awesome Company example configs Phase 1, Patch 2
+
+	  Example configuration files for a "basic PBX" deployment for the fictitious
+	  Super Awesome Company. Details at https://reviewboard.asterisk.org/r/4488/
+	  and https://wiki.asterisk.org/wiki/display/AST/Super+Awesome+Company
+
+	  Patch 4488 includes all functionality needed for SAC's outside connectivity
+	  and some externally accessed features, as well as outbound dialing.
+
+	  Reported by: Malcolm Davenport
+	  Tested by: Rusty Newton
+
+	  Review: https://reviewboard.asterisk.org/r/4488/
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@433624 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-03-27 16:04 +0000 [13557675d4]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_registrar_expire.c: Made use ao2 container template routines and eliminated some RAII_VAR() usage.
+
+	  * Converted the contact_autoexpire container to use the ao2 template hash
+	  and cmp functions.  Also made use the OBJ_SEARCH_xxx names instead of the
+	  deprecated names.
+
+	  * Eliminates several unnecessary uses of RAII_VAR().
+
+	  Review: https://reviewboard.asterisk.org/r/4524/
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@433622 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-03-27 15:30 +0000 [85feac857c]  Mark Michelson <mmichelson at digium.com>
+
+	* Add stateful PJSIP response API call, and use it for out-of-dialog responses.
+
+	  Asterisk had an issue where retransmissions of MESSAGE requests resulted in
+	  Asterisk processing the retransmission as if it were a new MESSAGE request.
+
+	  This patch fixes the issue by creating a transaction in PJSIP on the incoming
+	  request. This way, if a retransmission arrives, the PJSIP transaction layer
+	  will resend the response and Asterisk will not ever see the retransmission.
+
+	  ASTERISK-24920 #close
+	  Reported by Mark Michelson
+
+	  Review: https://reviewboard.asterisk.org/r/4532/
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@433619 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-03-27 12:50 +0000 [dc2cf21144]  Richard Mudgett <rmudgett at digium.com>
+
+	* res_pjsip_registrar_expire.c: Cleanup scheduler leaks on unload/shutdown.
+
+	  Contact expiration object refs were leaked when the module was unloaded.
+
+	  * Made empty the scheduler of entries before destroying it to release the
+	  object ref held by the scheduler entry.
+
+	  Review: https://reviewboard.asterisk.org/r/4523/
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@433596 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-03-27 09:41 +0000 [6e6f5b3a1f]  scsiguy (License 6692)
+
+	* res/res_timing_kqueue: Update the module to conform to current timer API
+
+	  This patch updates the kqueue timing module to conform to current timer API.
+
+	  This fixes issues with using the kqueue timing source on Asterisk 13 on
+	  FreeBSD 10. These issues include:
+
+	  - Remove support for kevent64().  The values used to support Asterisk timers
+	    fit within 32bits and so can be handled on all platforms via kevent().
+
+	  - Provide debug logging for, but do not track, unacked events.  This matches
+	    the behavior of all other timer implementations.
+
+	  - Implement continuous mode by triggering and leaving active, a user event.
+	    This ensures that the file descriptor for the timer returns immediately from
+	    poll(), without placing the load of a high speed timer on the kernel.
+
+	  - In kqueue_timer_get_max_rate(), don't overstate the capability of the timer.
+	    On some platforms, UINT_MAX is greater than INTPTR_MAX, the largest integer
+	    type kqueue supports for timers.
+
+	  - In kqueue_timer_get_event(), assume the caller woke up from poll() and just
+	    return the mode the timer is currently in. This matches all other timer
+	    implementations.
+
+	  - Adjust the test code now that unacked events are not tracked.
+
+	  Review: https://reviewboard.asterisk.org/r/4465/
+
+	  ASTERISK-24857 #close
+	  Reported by: scsiguy
+	  Tested by: Ed Hynan
+	  patches:
+	    rb4465.patch submitted by scsiguy (License 6692)
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@433574 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-03-27 07:26 +0000 [b0df413fb2]  Corey Farrell <git at cfware.com>
+
+	* Fix link error for utils/aelparse.
+
+	  Use the standard ast_log instead of ast_log_safe for STANDALONE programs.
+
+	  Review: https://reviewboard.asterisk.org/r/4538/
+	  ........
+
+	  Merged revisions 433549 from http://svn.asterisk.org/svn/asterisk/branches/11
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@433550 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-03-27 02:09 +0000 [d01706ce1e]  Corey Farrell <git at cfware.com>
+
+	* Improved and portable ast_log recursion avoidance
+
+	  This introduces a new logger routine ast_log_safe.  This routine should be
+	  used for all error messages in code that can be run as a result of ast_log.
+	  ast_log_safe does nothing if run recursively.  All error logging in
+	  astobj2.c, strings.c and utils.h have been switched to ast_log_safe.
+
+	  This required adding support for raw threadstorage.  This provides direct
+	  access to the void* pointer in threadstorage.  In ast_log_safe, NULL is used
+	  to signify that this thread is not already running ast_log_safe, (void*)1 when
+	  it is already running.  This was done since it's critical that ast_log_safe
+	  do nothing that could log during recursion checking.
+
+	  ASTERISK-24155 #close
+	  Reported by: Timo Teräs
+	  Review: https://reviewboard.asterisk.org/r/4502/
+	  ........
+
+	  Merged revisions 433522 from http://svn.asterisk.org/svn/asterisk/branches/11
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@433523 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-03-26 18:07 +0000 [4b225e2104]  Corey Farrell <git at cfware.com>
+
+	* Fix compile errors caused by r4500 / r4501.
+
+	  * Add ast_register_cleanup to utils/clicompat.c to deal with
+	    any utils that copy sources from main.
+	  * Asterisk 13+: remove unused variables from core_local.c.
+
+	  Review: https://reviewboard.asterisk.org/r/4534/
+	  ........
+
+	  Merged revisions 433499 from http://svn.asterisk.org/svn/asterisk/branches/11
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@433500 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-03-26 17:19 +0000 [6adf26f14d]  Corey Farrell <git at cfware.com>
+
+	* Replace most uses of ast_register_atexit with ast_register_cleanup.
+
+	  Since 'core stop now' and 'core restart now' do not stop modules,
+	  it is unsafe for most of the core to run cleanups.  Originally all
+	  cleanups used ast_register_atexit, and were only changed when it
+	  was shown to be unsafe.  ast_register_atexit is now used only when
+	  absolutely required to prevent corruption and close child processes.
+
+	  Exceptions that need to use ast_register_atexit:
+	  * CDR: Flush records.
+	  * res_musiconhold: Kill external applications.
+	  * AstDB: Close the DB.
+	  * canary_exit: Kill canary process.
+
+	  ASTERISK-24142 #close
+	  Reported by: David Brillert
+
+	  ASTERISK-24683 #close
+	  Reported by: Peter Katzmann
+
+	  ASTERISK-24805 #close
+	  Reported by: Badalian Vyacheslav
+
+	  ASTERISK-24881 #close
+	  Reported by: Corey Farrell
+
+	  Review: https://reviewboard.asterisk.org/r/4500/
+	  Review: https://reviewboard.asterisk.org/r/4501/
+	  ........
+
+	  Merged revisions 433495 from http://svn.asterisk.org/svn/asterisk/branches/11
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@433497 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-03-26 12:46 +0000 [d0df545a44]  Corey Farrell <git at cfware.com>
+
+	* res_pjsip: Enable unload of all modules at shutdown.
+
+	  * Move most of res_pjsip:module_unload to unload_pjsip to resolve crashes
+	    caused by running PJSIP functions from non-PJSIP threads.
+	  * Remove call to pjsip_endpt_destroy(ast_pjsip_endpoint), it was causing
+	    crashes in some cases.  In theory pj_shutdown() should take care of this.
+	  * Mark res_pjsip_keepalive and res_pjsip_session as allowed to unload at
+	    shutdown.
+	  * Resolve leaked config global in res_pjsip_notify.
+	  * Unregister pubsub pjsip service module.
+	  * Implement cleanup for res_pjsip_session.
+
+	  ASTERISK-24731 #close
+	  Reported by: Corey Farrell
+	  Review: https://reviewboard.asterisk.org/r/4498/
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@433469 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-03-26 12:04 +0000 [fd434a210f]  Kevin Harwell <kharwell at digium.com>
+
+	* app_confbridge: file playback blocks dtmf
+
+	  Attempting to execute DTMF in a confbridge while file playback (prompt,
+	  announcement, etc) is occurring is not allowed. You have to wait until
+	  the sound file has completed before entering DTMF. This patch fixes it
+	  so that app_confbridge now monitors for dtmf key presses during menu
+	  driven file playback. If a key is pressed playback stops and it executes
+	  the matched menu option.
+
+	  ASTERISK-24864 #close
+	  Reported by: Steve Pitts
+	  Review: https://reviewboard.asterisk.org/r/4510/
+	  ........
+
+	  Merged revisions 433445 from http://svn.asterisk.org/svn/asterisk/branches/11
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@433446 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-03-25 13:37 +0000 [dea885a607]  Richard Mudgett <rmudgett at digium.com>
+
+	* A couple minor cleanup tweaks.
+
+	  * In res/res_sorcery_realtime.c: Broke long line.
+
+	  * In main/bucket.c: Eliminated unnecessary NULL check as
+	  ast_sorcery_unref() is NULL tolerant and set the global object to NULL
+	  after unref in the system shutdown bucket_cleanup().
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@433420 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-03-25 10:30 +0000 [05de9082a5]  Simon Arlott (License 5756)
+
+	* res_xmpp: Buddies are always auto-registered when processing the roster
+
+	  Due to a quirk in the configuration handling of res_xmpp, the 'autoregister'
+	  setting was never actually processed. This was due to not properly copying
+	  over the global settings to the client settings when applying the
+	  configuration to the run-time object.
+
+	  Review: https://reviewboard.asterisk.org/r/4496/
+
+	  ASTERISK-14233
+	  ASTERISK-24780 #close
+	  Reported by: Simon Arlott
+	  patches:
+	    asterisk-13.1.0-24780 uploaded by Simon Arlott (License 5756)
+	  ........
+
+	  Merged revisions 433395 from http://svn.asterisk.org/svn/asterisk/branches/11
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@433396 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-03-24 14:26 +0000 [b1e9552b08]  Richard Mudgett <rmudgett at digium.com>
+
+	* chan_pjsip: Add "rpid_immediate" option to prevent unnecessary "180 Ringing" messages.
+
+	  Incoming PJSIP call legs that have not been answered yet send unnecessary
+	  "180 Ringing" or "183 Progress" messages every time a connected line
+	  update happens.  If the outgoing channel is also PJSIP then the incoming
+	  channel will always send a "180 Ringing" or "183 Progress" message when
+	  the outgoing channel sends the INVITE.
+
+	  Consequences of these unnecessary messages:
+
+	  * The caller can start hearing ringback before the far end even gets the
+	  call.
+
+	  * Many phones tend to grab the first connected line information and refuse
+	  to update the display if it changes.  The first information is not likely
+	  to be correct if the call goes to an endpoint not under the control of the
+	  first Asterisk box.
+
+	  When connected line first went into Asterisk in v1.8, chan_sip received an
+	  undocumented option "rpid_immediate" that defaults to disabled.  When
+	  enabled, the option immediately passes connected line update information
+	  to the caller in "180 Ringing" or "183 Progress" messages as described
+	  above.
+
+	  * Added "rpid_immediate" option to prevent unnecessary "180 Ringing" or
+	  "183 Progress" messages.  The default is "no" to disable sending the
+	  unnecessary messages.
+
+	  ASTERISK-24781 #close
+	  Reported by: Richard Mudgett
+
+	  Review: https://reviewboard.asterisk.org/r/4473/
+
+
+	  git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@433338 65c4cc65-6c06-0410-ace0-fbb531ad65f3
+
+2015-03-23  Asterisk Development Team <asteriskteam at digium.com>
+
+	* Asterisk 13.3.0-rc1 Released.
+
+2015-03-22 23:58 +0000 [r433247-433269]  Matthew Jordan <mjordan at digium.com>
+
+	* apps/app_queue.c, main/cli.c, main/cdr.c, main/manager.c,
+	  main/rtp_engine.c, /, funcs/func_cdr.c: Fix compilations errors
+	  on 64-bit OpenBSD systems In versiong 5.5, OpenBSD went to 64-bit
+	  time values. This requires a cast to (long) when printing members
+	  of certain time structs. Review:
+	  https://reviewboard.asterisk.org/r/4507 ASTERISK-24879 #close
+	  Reported by: snuffy Tested by: snuffy patches:
+	  openbsd-time64.diff uploaded by snuffy (License 5024) ........
+	  Merged revisions 433268 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+	* main/asterisk.c, main/loader.c, main/xmldoc.c, /: Fix compilation
+	  issues for OpenBSD This patch addresses compilation issues for
+	  OpenBSD. Specifically, it addresses: * It allows including
+	  <sys/vmmeter.h> in asterisk.c * Provides a needed (size_t) cast
+	  in xmldoc.c In 13+, it also addresses a conditional inclusion in
+	  loader.c. Review: https://reviewboard.asterisk.org/r/4506
+	  ASTERISK-24880 #close Reported by: snuffy Tested by: snuffy
+	  patches: misc-openbsd.diff uploaded by snuffy (License 5024)
+	  ........ Merged revisions 433245 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2015-03-20 19:52 +0000 [r433199-433222]  Richard Mudgett <rmudgett at digium.com>
+
+	* res/res_pjsip_messaging.c, res/res_pjsip/pjsip_options.c,
+	  res/res_pjsip.c, res/res_pjsip_nat.c: Audit
+	  ast_pjsip_rdata_get_endpoint() usage for ref leaks. Valgrind
+	  found some memory leaks associated with
+	  ast_pjsip_rdata_get_endpoint(). The leaks would manifest when
+	  sending responses to OPTIONS requests, processing MESSAGE
+	  requests, and res_pjsip supplements implementing the
+	  incoming_request callback. * Fix ast_pjsip_rdata_get_endpoint()
+	  endpoint ref leaks in res/res_pjsip.c:supplement_on_rx_request(),
+	  res/res_pjsip/pjsip_options.c:send_options_response(),
+	  res/res_pjsip_messaging.c:rx_data_to_ast_msg(), and
+	  res/res_pjsip_messaging.c:send_response(). * Eliminated
+	  RAII_VAR() use with ast_pjsip_rdata_get_endpoint() in
+	  res/res_pjsip_nat.c:nat_on_rx_message(). * Fixed inconsistent but
+	  benign return value in
+	  res/res_pjsip/pjsip_options.c:options_on_rx_request(). Review:
+	  https://reviewboard.asterisk.org/r/4511/
+
+	* res/res_pjsip_sdp_rtp.c, main/sorcery.c, main/xmldoc.c:
+	  res_pjsip_sdp_rtp,sorcery: Fix invalid access and memory leak
+	  respectively. Valgrind found a memory leak and invalid access. *
+	  Fix invalid access by sscanf() being fed a non-nul terminated
+	  string of digits in res/res_pjsip_sdp_rtp.c:get_codecs(). * Fix
+	  memory leak in main/sorcery.c:sorcery_object_field_destructor().
+	  * Fix potential NULL pointer dereference in
+	  main/xmldoc.c:xmldoc_get_syntax_config_option(). Review:
+	  https://reviewboard.asterisk.org/r/4513/
+
+2015-03-19 19:19 +0000 [r433174]  Matthew Jordan <mjordan at digium.com>
+
+	* funcs/func_env.c, tests/test_func_file.c, /: funcs/func_env: Fix
+	  regression caused in FILE read operation When r432935 was merged,
+	  it did correctly fix a situation where a FILE read operation on
+	  the middle of a file buffer would not read the requested length
+	  in the parameters passed to the FILE function. Unfortunately, it
+	  would also allow the FILE function to append more bytes than what
+	  was available in the buffer if the length exceeded the end of the
+	  buffer length. This patch takes the minimum of the remaining
+	  bytes in the buffer along with the calculated length to append
+	  provided by the original patch, and uses that as the length to
+	  append in the return result. This patch also updates the unit
+	  tests with the scenarios that were originally pointed out in
+	  ASTERISK-21765 that the original implementation treated
+	  incorrectly. ASTERISK-21765 ........ Merged revisions 433173 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2015-03-19 10:20 +0000 [r433113-433126]  Corey Farrell <git at cfware.com>
+
+	* main/logger.c, /: logger: Apply default console logging when
+	  configuration cannot be loaded. When logger.conf is missing or
+	  invalid enable console logging and display an error message.
+	  ASTERISK-24817 #close Reported by: Corey Farrell Review:
+	  https://reviewboard.asterisk.org/r/4497/ ........ Merged
+	  revisions 433122 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+	* channels/sip/include/dialog.h, channels/chan_sip.c,
+	  channels/sip/include/sip.h: chan_sip: Simplify dialog/peer
+	  references, improve REF_DEBUG output. * Replace functions for
+	  ref/undef of dialogs and peers with macro's to call
+	  ao2_t_bump/ao2_t_cleanup. * Enable passthough of REF_DEBUG caller
+	  information to sip_alloc and find_call. ASTERISK-24882 #close
+	  Reported by: Corey Farrell Review:
+	  https://reviewboard.asterisk.org/r/4189/
+
+	* /, channels/chan_sip.c: chan_sip: Fix dialog reference leaked to
+	  scheduler for reinvite_timeout. Release the scheduler reference
+	  to the dialog for reinvite timeout during dialog_unlink_all.
+	  ASTERISK-24876 #close Reported by: Corey Farrell Review:
+	  https://reviewboard.asterisk.org/r/4491/ ........ Merged
+	  revisions 433112 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2015-03-18 02:34 +0000 [r433088]  Richard Mudgett <rmudgett at digium.com>
+
+	* res/res_pjsip_session.c: res_pjsip_session: Fix off-nominal extra
+	  unref of session.
+
+2015-03-17 22:15 +0000 [r433060-433064]  Scott Griepentrog <sgriepentrog at digium.com>
+
+	* main/asterisk.c, main/config.c, main/xmldoc.c, main/manager.c,
+	  include/asterisk/config.h, main/utils.c, main/codec_builtin.c,
+	  main/endpoints.c: Various: bugfixes found via chaos Using
+	  DEBUG_CHAOS several instances of a null pointer crash, and one
+	  uninitialized variable were uncovered and fixed. Also added
+	  details on why Asterisk failed to initialize. Review:
+	  https://reviewboard.asterisk.org/r/4468/
+
+	* build_tools/cflags.xml, include/asterisk/utils.h: core: Introduce
+	  chaos into memory allocations Locate potential crashes by
+	  exercising seldom used code paths. This patch introduces a new
+	  define DEBUG_CHAOS, and mechanism to randomly return an error
+	  condition from functions that will seldom do so. Functions that
+	  handle the allocation of memory get the first treatment. Review:
+	  https://reviewboard.asterisk.org/r/4463/
+
+2015-03-17 21:49 +0000 [r433057]  Richard Mudgett <rmudgett at digium.com>
+
+	* main/netsock2.c, /, res/res_pjsip_sdp_rtp.c, res/res_pjsip_t38.c,
+	  apps/app_externalivr.c, res/res_pjsip_acl.c: Audit
+	  ast_sockaddr_resolve() usage for memory leaks. Valgrind found
+	  some memory leaks associated with ast_sockaddr_resolve(). Most of
+	  the leaks had already been fixed by earlier memory leak hunt
+	  patches. This patch performs an audit of ast_sockaddr_resolve()
+	  and found one more. * Fix ast_sockaddr_resolve() memory leak in
+	  apps/app_externalivr.c:app_exec(). * Made
+	  main/netsock2.c:ast_sockaddr_resolve() always set the addrs
+	  parameter for safety so the pointer will never be uninitialized
+	  on return. The same goes for
+	  res/res_pjsip_acl.c:extract_contact_addr(). * Made functions that
+	  call ast_sockaddr_resolve() with RAII_VAR() controlling the addrs
+	  variable use ast_free instead of ast_free_ptr to provide better
+	  MALLOC_DEBUG information. Review:
+	  https://reviewboard.asterisk.org/r/4509/ ........ Merged
+	  revisions 433056 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2015-03-17 18:34 +0000 [r433028-433031]  Kevin Harwell <kharwell at digium.com>
+
+	* include/asterisk/res_pjsip.h,
+	  res/res_pjsip_endpoint_identifier_anonymous.c,
+	  res/res_pjsip_endpoint_identifier_ip.c, res/res_pjsip.c,
+	  res/res_pjsip_endpoint_identifier_user.c: res_pjsip: Allow
+	  configuration of endpoint identifier query order Updated some
+	  documentation stating that endpoint identifiers registered
+	  without a name are place at the front of the lookup list. Also
+	  renamed register method
+	  'ast_sip_register_endpoint_identifier_by_name' to
+	  'ast_sip_register_endpoint_identifier_with_name' ASTERISK-24840
+	  Reported by: Mark Michelson
+
+	* configs/samples/pjsip.conf.sample, CHANGES, res/res_pjsip.c,
+	  res/res_pjsip_endpoint_identifier_user.c,
+	  include/asterisk/res_pjsip.h, res/res_pjsip/config_global.c,
+	  res/res_pjsip_endpoint_identifier_anonymous.c,
+	  res/res_pjsip_endpoint_identifier_ip.c,
+	  contrib/ast-db-manage/config/versions/45e3f47c6c44_add_pjsip_endpoint_identifier_order.py:
+	  res_pjsip: Allow configuration of endpoint identifier query order
+	  This patch fixes previously reverted code that caused binary
+	  incompatibility problems with some modules. And like the original
+	  patch it makes sure that no matter what order the endpoint
+	  identifier modules were loaded, priority is given based on the
+	  ones specified in the new global 'endpoint_identifier_order'
+	  option. ASTERISK-24840 Reported by: Mark Michelson Review:
+	  https://reviewboard.asterisk.org/r/4489/
+
+2015-03-17 16:10 +0000 [r433005]  Richard Mudgett <rmudgett at digium.com>
+
+	* res/res_pjsip.c: res_pjsip: Add reason comment.
+
+2015-03-14 02:28 +0000 [r432971]  Matthew Jordan <mjordan at digium.com>
+
+	* /, main/format_cap.c: main/frame: Don't report empty disallow
+	  values as an error In realtime, it is normal to have a database
+	  with both 'allow' and 'disallow' columns in the schema. It is
+	  perfectly valid to have an 'allow' value of '!all,g722,ulaw,alaw'
+	  and no 'disallow' value. Unlike in static conf files, you can't
+	  *not* provide the disallow value. Thus, the empty disallow value
+	  causes a spurious WARNING message, which is kind of annoying.
+	  This patch makes it so that a 'disallow' value with no ... value
+	  ... is ignored. Granted, you can still screw this up as well, as
+	  technically specifying 'disallow=all,!ulaw' allows only ulaw, and
+	  then you would have no 'allow' value in your database. But
+	  really, why would you do that? WHY? ASTERISK-16779 #close
+	  Reported by: Atis Lezdins ........ Merged revisions 432970 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2015-03-14 02:00 +0000 [r432945-432949]  Joshua Colp <jcolp at digium.com>
+
+	* funcs/func_curl.c, /: func_curl: Don't hold exclusive lock when
+	  performing HTTP request. This code originally kept a lock held
+	  when performing the HTTP request to ensure that the options
+	  provided to curl remain valid. This doesn't seem to be necessary
+	  these days and holding the lock caused requests to happen
+	  sequentially instead of in parallel. ASTERISK-18708 #close
+	  Reported by: Dave Cabot ........ Merged revisions 432948 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+	* main/cli.c, /: core: Fix tab completion of "core set debug
+	  channel" CLI command. The "core set debug channel" CLI command
+	  mistakenly had source filenames added to its tab completion. This
+	  occurred because the CLI generator fell back to the "core set
+	  debug" command which permits setting debug at a source filename
+	  level. ASTERISK-21038 #close Reported by: Richard Kenner ........
+	  Merged revisions 432944 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2015-03-14 01:21 +0000 [r432920-432938]  Matthew Jordan <mjordan at digium.com>
+
+	* /, funcs/func_env.c: FILE: fix retrieval of file contents when
+	  offset is specified The loop that reads in a file was not
+	  correctly using the offset when determining what bytes to append
+	  to the output. This patch corrects the logic such that the
+	  correct portion of the file is extracted when an offset is
+	  specified. ASTERISK-21765 Reported by: John Zhong Tested by: Matt
+	  Jordan, Di-Shi Sun patches: file_read_390821.patch uploaded by
+	  Di-Shi Sun (License 5076) ........ Merged revisions 432935 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+	* apps/app_amd.c, /, configs/samples/amd.conf.sample: apps/app_amd:
+	  Document maximum_word_length option; fix AMDCAUSE documentation
+	  This patch corrects the documentation for the AMD application.
+	  Specifically: * It documents the maximum_word_length option,
+	  which limits the maximum allowed length of a single utterance. *
+	  It clarifies the AMDCAUSE values MAXWORDS and MAXWORDLENGTH.
+	  MAXWORDLENGTH was documented as MAXWORDS, while MAXWORDS was
+	  undocumented. Thanks to the issue reporter, Frank DiGennaro, for
+	  pointing out the issues. ASTERISK-19470 #close Reported by: Frank
+	  DiGennaro ........ Merged revisions 432918 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2015-03-13 17:04 +0000 [r432892-432894]  Richard Mudgett <rmudgett at digium.com>
+
+	* res/res_pjsip/pjsip_configuration.c: chan_pjsip: AMI action
+	  PJSIPShowEndpoint closes AMI connection on error. Also fixed
+	  similar problem with AMI action PJSIPShowEndpoints.
+	  ASTERISK-24872 #close Reported by: Dmitriy Serov Review:
+	  https://reviewboard.asterisk.org/r/4487/
+
+	* channels/chan_pjsip.c, res/res_pjsip_caller_id.c:
+	  chan_pjsip/res_pjsip_callerid: Make Party ID handling simpler and
+	  consistent. The res_pjsip modules were manually checking both
+	  name and number presentation values when there is a function that
+	  determines the combined presentation for a party ID struct. The
+	  function takes into account if the name or number components are
+	  valid while the manual code rarely checked if the data was even
+	  valid. * Made use ast_party_id_presentation() rather than
+	  manually checking party ID presentation values. * Ensure that
+	  set_id_from_pai() and set_id_from_rpid() will not return
+	  presentation values other than what is pulled out of the SIP
+	  headers. It is best if the code doesn't assume that
+	  AST_PRES_ALLOWED and AST_PRES_USER_NUMBER_UNSCREENED are zero. *
+	  Fixed copy paste error in add_privacy_params() dealing with RPID
+	  privacy. * Pulled the id->number.valid test from
+	  add_privacy_header() and add_privacy_params() up into the parent
+	  function add_id_headers() to skip adding PAI/RPID headers
+	  earlier. * Made update_connected_line_information() not send out
+	  connected line updates if the connected line number is invalid.
+	  Lower level code would not add the party ID information and thus
+	  the sent message would be unnecessary. * Eliminated RAII_VAR
+	  usage in send_direct_media_request(). Review:
+	  https://reviewboard.asterisk.org/r/4472/
+
+2015-03-13 14:48 +0000 [r432868]  Kevin Harwell <kharwell at digium.com>
+
+	* include/asterisk/res_pjsip.h, res/res_pjsip/config_global.c,
+	  res/res_pjsip_endpoint_identifier_anonymous.c,
+	  res/res_pjsip_endpoint_identifier_ip.c,
+	  contrib/ast-db-manage/config/versions/45e3f47c6c44_add_pjsip_endpoint_identifier_order.py,
+	  configs/samples/pjsip.conf.sample, CHANGES, res/res_pjsip.c,
+	  res/res_pjsip_endpoint_identifier_user.c: Revert - res_pjsip:
+	  Allow configuration of endpoint identifier query order Due to a
+	  break in binary compatibility with some other modules these
+	  changes are being reverted until the issue can be resolved.
+	  ASTERISK-24840 Reported by: Mark Michelson
+
+2015-03-12 12:58 +0000 [r432808-432811]  Matthew Jordan <mjordan at digium.com>
+
+	* /, main/audiohook.c: main/audiohook: Update internal sample rate
+	  on reads When an audiohook is created (which is used by the
+	  various Spy applications and Snoop channel in Asterisk 13+), it
+	  initially is given a sample rate of 8kHz. It is expected,
+	  however, that this rate may change based on the media that passes
+	  through the audiohook. However, the read/write operations on the
+	  audiohook behave very differently. When a frame is written to the
+	  audiohook, the format of the frame is checked against the
+	  internal sample rate. If the rate of the format does not match
+	  the internal sample rate, the internal sample rate is updated and
+	  a new SLIN format is chosen based on that sample rate. This works
+	  just fine. When a frame is read, however, we do something quite
+	  different. If the format rate matches the internal sample rate,
+	  all is fine. However, if the rates don't match, the audiohook
+	  attempts to "fix up" the number of samples that were requested.
+	  This can result in some seriously large number of samples being
+	  requested from the read/write factories. Consider the worst case
+	  - 192kHz SLIN. If we attempt to read 20ms worth of audio produced
+	  at that rate, we'd request 3840 samples (192000 / (1000 / 20)).
+	  However, if the audiohook is still expecting an internal sample
+	  rate of 8000, we'll attempt to "fix up" the requested samples to:
+	  samples_converted = samples * (ast_format_get_sample_rate(format)
+	  / (float) audiohook->hook_internal_samp_rate); which is: 92160 =
+	  3840 * (192000 / 8000) This results in us attempting to read
+	  92160 samples from our factories, as opposed to the 3840 that we
+	  actually wanted. On a 64-bit machine, this miraculously survives
+	  - despite allocating up to two buffers of length 92160 on the
+	  stack. The 32-bit machines aren't quite so lucky. Even in the
+	  case where this works, we will either (a) get way more samples
+	  than we wanted; or (b) get about 3840 samples, assuming the
+	  timing is pretty good on the machine. Either way, the calculation
+	  being performed is wrong, based on the API users expectations. My
+	  first inclination was to allocate the buffers on the heap. As it
+	  is, however, there's at least two drawbacks with doing this: (1)
+	  It's a bit complicated, as the size of the buffers may change
+	  during the lifetime of the audiohook (ew). (2) The stack is
+	  faster (yay); the heap is slower (boo). Since our calculation is
+	  flat out wrong in the first place, this patch fixes this issue by
+	  instead updating the internal sample rate based on the format
+	  passed into the read operation. This causes us to read the
+	  correct number of samples, and has the added benefit of setting
+	  the audihook with the right SLIN format. Note that this issue was
+	  caught by the Asterisk Test Suite as a result of r432195 in the
+	  13 branch. Because this issue is also theoretically possible in
+	  Asterisk 11, the change is being made here as well. Review:
+	  https://reviewboard.asterisk.org/r/4475/ ........ Merged
+	  revisions 432810 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+	* Makefile, include/asterisk/utils.h, /, configure, main/Makefile,
+	  configure.ac, include/asterisk/inline_api.h, makeopts.in: Add
+	  support for the clang compiler; update RAII_VAR to use
+	  BlocksRuntime RAII_VAR, which is used extensively in Asterisk to
+	  manage reference counted resources, uses a GCC extension to
+	  automatically invoke a cleanup function when a variable loses
+	  scope. While this functionality is incredibly useful and has
+	  prevented a large number of memory leaks, it also prevents
+	  Asterisk from being compiled with clang. This patch updates the
+	  RAII_VAR macro such that it can be compiled with clang. It makes
+	  use of the BlocksRuntime, which allows for a closure to be
+	  created that performs the actual cleanup. Note that this does not
+	  attempt to address the numerous warnings that the clang compiler
+	  catches in Asterisk. Much thanks for this patch goes to: * The
+	  folks on StackOverflow who asked this question and Leushenko for
+	  providing the answer that formed the basis of this code:
+	  http://stackoverflow.com/questions/24959440/rewrite-gcc-cleanup-macro-with-nested-function-for-clang
+	  * Diederik de Groot, who has been extremely patient in working on
+	  getting this patch into Asterisk. Review:
+	  https://reviewboard.asterisk.org/r/4370/ ASTERISK-24133
+	  ASTERISK-23666 ASTERISK-20399 ASTERISK-20850 #close Reported by:
+	  Diederik de Groot patches: RAII_CLANG.patch uploaded by Diederik
+	  de Groot (License 6600) ........ Merged revisions 432807 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2015-03-11 16:38 +0000 [r432764-432787]  Richard Mudgett <rmudgett at digium.com>
+
+	* res/res_pjsip/config_domain_aliases.c,
+	  res/res_pjsip/include/res_pjsip_private.h,
+	  include/asterisk/res_pjsip.h: res_pjsip: Move internal
+	  init/destroy prototypes to private header file. Done as a
+	  separate commit from a finding in
+	  https://reviewboard.asterisk.org/r/4467/
+
+	* include/asterisk/res_pjsip.h, res/res_pjsip/config_global.c,
+	  res/res_pjsip/pjsip_configuration.c: res_pjsip: Fix pjsip.conf
+	  type=global object default value handling. When a type=global
+	  section is not defined in pjsip.conf the global defaults are not
+	  applied. As a result the mandatory Max-Forwards header is not
+	  added to SIP messages for res_pjsip/chan_pjsip. The handling of
+	  pjsip.conf type=global objects has several problems: 1) If the
+	  global object is missing the defaults are not applied. 2) If the
+	  global object is missing the default_outbound_endpoint's default
+	  value is not returned by
+	  ast_sip_global_default_outbound_endpoint(). 3) Defines are needed
+	  so default values only need to be changed in one place. * Added a
+	  sorcery instance observer callback to check if there were any
+	  type=global sections loaded. If there were more than one then
+	  issue an error message. If there were none then apply the global
+	  defaults. * Fixed ast_sip_global_default_outbound_endpoint() to
+	  return the documented default when no type=global object is
+	  defined. * Made defines for the global default values. *
+	  Increased the default_useragent[] size because SVN version
+	  strings can get lengthy and 128 characters may not be enough. *
+	  Fixed an off-nominal code path ref leak in global_alloc() if the
+	  string fields fail to initialize. * Eliminated RAII_VAR in
+	  get_global_cfg() and ast_sip_global_default_outbound_endpoint().
+	  ASTERISK-24807 #close Reported by: Anatoli Review:
+	  https://reviewboard.asterisk.org/r/4467/
+
+	* res/res_pjsip/pjsip_global_headers.c: res_pjsip: Fixed invalid
+	  empty Server and User-Agent SIP headers. Setting pjsip.conf
+	  useragent to an empty string results in an empty SIP header being
+	  sent. * Made not add an empty SIP header item to the global SIP
+	  headers list. Review: https://reviewboard.asterisk.org/r/4467/
+
+2015-03-10 23:09 +0000 [r432742]  Joshua Colp <jcolp at digium.com>
+
+	* main/stasis_channels.c, main/endpoints.c, main/stasis_bridges.c:
+	  core: Don't create snapshots with locks. Snapshots are immutable
+	  and are never changed. Allocating them with a lock is wasteful.
+	  Review: https://reviewboard.asterisk.org/r/4469/
+
+2015-03-10 21:33 +0000 [r432693-432721]  Matthew Jordan <mjordan at digium.com>
+
+	* res/res_config_odbc.c, /: res/res_config_odbc: Fix improper
+	  escaping of backslashes with MySQL When escaping backslashes with
+	  MySQL, the proper way to escape the characters in a LIKE clause
+	  is to escape the '\' four times, i.e., '\\\\'. To quote the MySQL
+	  manual: "Because MySQL uses C escape syntax in strings (for
+	  example, “\n” to represent a newline character), you must double
+	  any “\” that you use in LIKE strings. For example, to search for
+	  “\n”, specify it as “\\n”. To search for “\”, specify it as
+	  “\\\\”; this is because the backslashes are stripped once by the
+	  parser and again when the pattern match is made, leaving a single
+	  backslash to be matched against." ASTERISK-24808 #close Reported
+	  by: Javier Acosta patches: res_config_odbc.diff uploaded by
+	  Javier Acosta (License 6690) ........ Merged revisions 432720
+	  from http://svn.asterisk.org/svn/asterisk/branches/11
+
+	* apps/app_voicemail.c, /: app_voicemail: Fix crash with IMAP
+	  backends when greetings aren't present When an IMAP backend is in
+	  use and greetings are set to be used, but aren't present for a
+	  user in their IMAP folder, Asterisk will crash. This occurs due
+	  to the mailstream being set to the 'greetings' folder and being
+	  left in that particular state, regardless of the success/failure
+	  of the attempt to access the folder the mailstream points to.
+	  Later access of the mailstream assumes that it points to the
+	  'INBOX' (or some other folder), resulting in either a crash (if
+	  the greetings folder didn't exist and the mailstream is invalid)
+	  or an inability to read messages from the 'INBOX' folder. This
+	  patch restores the mailstream to its correct state after
+	  accessing the greetings. This fixes the crash, and sets the
+	  mailstream to the state that VoiceMailMain expects. Note that
+	  while ASTERISK-23390 also contained a patch for this issue, the
+	  patch on ASTERISK-24786 is the one being merged here. Review:
+	  https://reviewboard.asterisk.org/r/4459/ ASTERISK-23390 #close
+	  Reported by: Ben Smithurst ASTERISK-24786 #close Reported by:
+	  Graham Barnett Tested by: Graham Barnett patches:
+	  app_voicemail.c.patch.SIGSEGV3rev2 uploaded by Graham Barnett
+	  (License 6685) ........ Merged revisions 432695 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+	* /, main/stdtime/localtime.c: localtime: Fix file descriptor leak
+	  on kqueue(2) systems The localtime management in the Asterisk
+	  core contains a thread that watches for changes in the local
+	  timezone. On systems where the directory containing
+	  /etc/localtime is modified frequently, the thread monitoring the
+	  changes will be woken up to determine if any changes in timezone
+	  have occurred. When using kqueue(2), this can cause a leak of
+	  file descriptors due to some improper management of resources.
+	  This patch updates the kqueue(2) handling in localtime, such that
+	  is no longer leaks resources. Review:
+	  https://reviewboard.asterisk.org/r/4450/ ASTERISK-24739 #close
+	  Reported by: Ed Hynan patches: 11.15.0-u.diff uploaded by Ed
+	  Hynan (Licnese 6680) 11.7.0-u.diff uploaded by Ed Hynan (License
+	  6680) svn-trunk-Jan-26-2015-u.diff uploaded by Ed Hynan (License
+	  6680) ........ Merged revisions 432691 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2015-03-10 16:04 +0000 [r432668]  Richard Mudgett <rmudgett at digium.com>
+
+	* res/res_pjsip_refer.c, res/res_pjsip_session.c,
+	  include/asterisk/res_pjsip_session.h,
+	  res/res_pjsip_session.exports.in: res_pjsip_refer: Fix occasional
+	  unexpected BYE sent after receiving a REFER. A race condition
+	  happened between initiating a transfer and requesting that a
+	  dialog termination be delayed. Occasionally, the transferrer
+	  channels would exit the bridge and hangup before the dialog
+	  termination delay was requested. * Made request dialog
+	  termination delay before initiating the transfer action. If the
+	  transfer fails then cancel the delayed dialog termination
+	  request. ASTERISK-24755 #close Reported by: John Bigelow Review:
+	  https://reviewboard.asterisk.org/r/4460/
+
+2015-03-09 16:12 +0000 [r432638]  Kevin Harwell <kharwell at digium.com>
+
+	* include/asterisk/res_pjsip.h, res/res_pjsip/config_global.c,
+	  res/res_pjsip_endpoint_identifier_anonymous.c,
+	  res/res_pjsip_endpoint_identifier_ip.c,
+	  contrib/ast-db-manage/config/versions/45e3f47c6c44_add_pjsip_endpoint_identifier_order.py
+	  (added), configs/samples/pjsip.conf.sample, CHANGES,
+	  res/res_pjsip.c, res/res_pjsip_endpoint_identifier_user.c:
+	  res_pjsip: Allow configuration of endpoint identifier query order
+	  It's possible to have a scenario that will create a conflict
+	  between endpoint identifiers. For instance an incoming call could
+	  be identified by two different endpoint identifiers and the one
+	  chosen depended upon which identifier module loaded first. This
+	  of course causes problems when, for example, the incoming call is
+	  expected to be identified by username, but instead is identified
+	  by ip. This patch adds a new 'global' option to res_pjsip called
+	  'endpoint_identifier_order'. It is a comma separated list of
+	  endpoint identifier names that specifies the order by which
+	  identifiers are processed and checked. ASTERISK-24840 #close
+	  Reported by: Mark Michelson Review:
+	  https://reviewboard.asterisk.org/r/4455/
+
+2015-03-08 01:46 +0000 [r432614]  Joshua Colp <jcolp at digium.com>
+
+	* res/res_rtp_asterisk.c: res_rtp_asterisk: Fix wrongful use of
+	  USE_PJPROJECT define. As pjproject is now used as a shared
+	  library a different define, HAVE_PJPROJECT, is used to specify if
+	  pjproject is present. ASTERISK-24830 #close Reported by: Stefan
+	  Engström
+
+2015-03-06 22:50 +0000 [r432574-432594]  Richard Mudgett <rmudgett at digium.com>
+
+	* res/res_pjsip_refer.c: res_pjsip_refer: Make safely get the
+	  context for a blind transfer. Made safely get the
+	  TRANSFER_CONTEXT channel value while the channel is locked in
+	  refer_incoming_attended_request() and
+	  refer_incoming_blind_request(). The pointer returned by
+	  pbx_builtin_getvar_helper() is only valid while the channel is
+	  locked.
+
+	* res/res_pjsip_refer.c: res_pjsip_refer: Made
+	  refer_attended_alloc() not create the ao2 object with a lock. The
+	  lock is unused.
+
+2015-03-06 21:11 +0000 [r432556]  Jonathan Rose <jrose at digium.com>
+
+	* include/asterisk/app.h, main/app.c: app: Add functions to swap
+	  voicemail function table for testing purposes
+
+2015-03-06 20:18 +0000 [r432528-432534]  Richard Mudgett <rmudgett at digium.com>
+
+	* channels/chan_dahdi.c, channels/sig_analog.c, /,
+	  channels/chan_dahdi.h, channels/sig_analog.h, UPGRADE.txt:
+	  chan_dahdi/sig_analog: Fix distinctive ring detection to suck
+	  less. The distinctive ring feature interferes with detecting
+	  Caller ID and appears to have been broken for years. What happens
+	  is if you have a ring-ring cadence as used in the UK you get too
+	  many DAHDI events for the distinctive ring pattern array and
+	  Caller ID detection is aborted. I think when Zapata/DAHDI added
+	  the ring begin event it broke distinctive ring. More events
+	  happen than before and the code does no filtering of which event
+	  times are recorded in the pattern array. * Made distinctive ring
+	  only record the ringt count when the ring ends instead of on just
+	  any DAHDI event. Distinctive ring can be ring, ring-ring,
+	  ring-ring-ring, or different ring durations for the up to three
+	  rings. * Fixed the distinctive ring detection enable
+	  (chan_dahdi.conf option usedistinctiveringdetection) to be per
+	  port instead of somewhat per port and somewhat global. This has
+	  been broken since v1.8. * Fixed using the default distinctive
+	  ring context when the detected pattern does not match any
+	  configured dringX patterns. The default context did not get set
+	  when the previous call was a matched distinctive ring pattern and
+	  the current call is not matched. This has been broken since v1.8.
+	  * Made distinctive ring have no effect on Caller ID detection
+	  when it is disabled. Caller ID detection just monitors for 10
+	  seconds before giving up. * Fixed leak of struct callerid_state
+	  memory when a polarity reversal during Caller ID detection causes
+	  the incoming call to be aborted. DAHDI-1143 AST-1545
+	  ASTERISK-24825 #close Reported by: Richard Mudgett ASTERISK-17588
+	  Reported by: Daniel Flounders Review:
+	  https://reviewboard.asterisk.org/r/4444/ ........ Merged
+	  revisions 432530 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+	* /, channels/chan_sip.c: chan_sip: Fix realtime locking inversion
+	  when poking a just built peer. When a realtime peer is built it
+	  can cause a locking inversion when the just built peer is poked.
+	  If the CLI command "sip show channels" is periodically executed
+	  then a deadlock can happen because of the locking inversion. *
+	  Push the peer poke off onto the scheduler thread to avoid the
+	  locking inversion of the just built realtime peer. AST-1540
+	  ASTERISK-24838 #close Reported by: Richard Mudgett Review:
+	  https://reviewboard.asterisk.org/r/4454/ ........ Merged
+	  revisions 432526 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2015-03-05 16:38 +0000 [r432485]  George Joseph <george.joseph at fairview5.com>
+
+	* apps/app_voicemail.c, /: app_voicemail: Fix compile breaking in
+	  app_voicemail with IMAP_STORAGE. There is a leftover "assert" in
+	  app_voicemail/__messagecount that references variables that don't
+	  exist. This causes the compile to fail when --enable-dev-mode and
+	  IMAP_STORAGE are selected. This patch removes the assert.
+	  Tested-by: George Joseph Review:
+	  https://reviewboard.asterisk.org/r/4461/ ........ Merged
+	  revisions 432484 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2015-03-04 18:52 +0000 [r432453]  Matthew Jordan <mjordan at digium.com>
+
+	* main/translate.c: translate: Prevent invalid memory accesses on
+	  fast shutdown When a 'core restart now' or 'core stop now' is
+	  executed and a channel is currently in a media operation, the
+	  translator matrix can be destroyed while a channel is currently
+	  blocked on getting the best translation choice (see
+	  ast_translator_best_choice). When the channel gets the mutex, the
+	  translation matrix now has invalid memory, and Asterisk crashes.
+	  This patch does two things: (1) We now only clean up the
+	  translation matrix on a graceful shutdown. In that case, there
+	  are no channels, and so there is no risk of this occurring. (2)
+	  We also now set the __matrix and __indextable to NULL. In some
+	  initial backtraces when this occurred, it looked as if there was
+	  a memory corruption occurring, and it wasn't until we determined
+	  that something had restarted Asterisk that the issue became
+	  clear. By setting these to NULL on shutdown, it becomes a bit
+	  easier to determine why a crash is occurring. Note that we could
+	  litter the code with NULL checks on the __matrix, but the act of
+	  making the translation matrix cleaned up on shutdown should
+	  preclude this issue from occurring in the first place, and this
+	  part of the code needs to be as fast as possible. Review:
+	  https://reviewboard.asterisk.org/r/4457/
+
+2015-03-02 19:14 +0000 [r432423]  Matthew Jordan <mjordan at digium.com>
+
+	* res/res_pjsip_sdp_rtp.c: res/res_pjsip_sdp_rtp: Revert portion of
+	  r432195 Unfortunately, while initial testing with ConfBridge did
+	  not reproduce the audio problem alluded to in the comment in
+	  res_pjsip_sdp_rtp, further testing did show that bridge_softmix
+	  and/or ConfBridge has a severe problem bridging two or more
+	  participants at different sampling rates. Sometimes, it even
+	  picks odd sampling rates that cause hideous audio problems. This
+	  patch backs out the offending portion of the code until the
+	  issues in the affected bridging modules can be more properly
+	  analyzed. ASTERISK-24841
+
+2015-02-27 18:23 +0000 [r432404]  Richard Mudgett <rmudgett at digium.com>
+
+	* main/json.c, rest-api/api-docs/endpoints.json,
+	  res/ari/resource_endpoints.c, res/res_ari_endpoints.c,
+	  include/asterisk/json.h, res/ari/resource_channels.c: ARI: Fix
+	  crash if integer values used in JSON payload 'variables' object.
+	  Sending the following ARI commands caused Asterisk to crash if
+	  the JSON body 'variables' object passes values of types other
+	  than strings. POST /ari/channels POST /ari/channels/{channelid}
+	  PUT /ari/endpoints/sendMessage PUT
+	  /ari/endpoints/{tech}/{resource}/sendMessage * Eliminated
+	  RAII_VAR usage in ast_ari_channels_originate_with_id(),
+	  ast_ari_channels_originate(), ast_ari_endpoints_send_message(),
+	  and ast_ari_endpoints_send_message_to_endpoint(). ASTERISK-24751
+	  #close Reported by: jeffrey putnam Review:
+	  https://reviewboard.asterisk.org/r/4447/
+
+2015-02-26 18:52 +0000 [r432385]  Scott Griepentrog <sgriepentrog at digium.com>
+
+	* include/asterisk/dial.h, main/dial.c: Dial API: add self destruct
+	  option when complete This patch adds a self-destruction option to
+	  the dial api. The usefulness of this is mostly when using async
+	  mode to spawn a separate thread used to handle the new call,
+	  while the calling thread is allowed to go on about other
+	  business. The only alternative to this option would be the
+	  calling thread spawning a new thread, or hanging around itself
+	  waiting to destroy the dial struct after completion. Example of
+	  use (minus error checking): struct ast_dial *dial =
+	  ast_dial_create(); ast_dial_append(dial, "PJSIP", "200", NULL);
+	  ast_dial_option_global_enable(dial, AST_DIAL_OPTION_ANSWER_EXEC,
+	  "Echo"); ast_dial_option_global_enable(dial,
+	  AST_DIAL_OPTION_SELF_DESTROY, NULL); ast_dial_run(dial, NULL, 1);
+	  The dial_run call will return almost immediately after spawning
+	  the new thread to run and monitor the dial. If the call is
+	  answered, it is placed into the echo app. When completed, it will
+	  call ast_dial_destroy() on the dial structure. Note that any
+	  allocations made to pass values to ast_dial_set_user_data() or
+	  dial options must be free'd in a state callback function on any
+	  of: AST_DIAL_RESULT_UNASWERED, AST_DIAL_RESULT_ANSWERED,
+	  AST_DIAL_RESULT_HANGUP, or AST_DIAL_RESULT_TIMEOUT. Review:
+	  https://reviewboard.asterisk.org/r/4443/
+
+2015-02-26 17:07 +0000 [r432363]  Kevin Harwell <kharwell at digium.com>
+
+	* /, apps/app_chanspy.c, main/channel.c: app_chanspy, channel: fix
+	  frame leaks Fixed a couple of frame leaks that were found during
+	  testing. ASTERISK-24828 #close Reported by: John Hardin Review:
+	  https://reviewboard.asterisk.org/r/4445/ ........ Merged
+	  revisions 432362 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2015-02-26 04:58 +0000 [r432321-432342]  Matthew Jordan <mjordan at digium.com>
+
+	* /, apps/Makefile, channels/Makefile: make: Remove 'res_features'
+	  from libraries to link against with cygwin/mingw32 Both the apps
+	  and channels Makefiles still listed 'res_features' as modules to
+	  link against when compiling for cygwin or mingw32. This module
+	  hasn't existed for quite some time. ASTERISK-18105 #close
+	  Reported by: feyfre ........ Merged revisions 432341 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+	* /, channels/chan_sip.c: channels/chan_sip: Don't send a BYE after
+	  final response when PBX thread fails When Asterisk fails to start
+	  a PBX thread for a new channel - for example, when the maxcalls
+	  setting in asterisk.conf is exceeded - we currently send a final
+	  response, and then attempt to send a BYE request to the UA. Since
+	  that's all sorts of wrong, this patch fixes that by setting
+	  sipalreadygone on the sip_pvt such that we don't get stuck
+	  sending BYE requests to something that does not want it. Note
+	  that this patch is a slight modification of the one on
+	  ASTERISK-15434. For clarity, it explicitly calls sipalreadygone
+	  with the calls to transmit a final response. ASTERISK-21845
+	  ASTERISK-15434 #close Reported by: Makoto Dei Tested by: Matt
+	  Jordan patches: sip-pbxstart-failed.patch uploaded by Makoto Dei
+	  (License 5027) ........ Merged revisions 432320 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2015-02-25 23:48 +0000 [r432301]  Rusty Newton <rnewton at digium.com>
+
+	* configs/basic-pbx/README (added), configs/basic-pbx (added),
+	  configs/basic-pbx/extensions.conf (added),
+	  configs/basic-pbx/logger.conf (added),
+	  configs/basic-pbx/indications.conf (added),
+	  configs/basic-pbx/musiconhold.conf (added),
+	  configs/basic-pbx/asterisk.conf (added),
+	  configs/basic-pbx/pjsip.conf (added),
+	  configs/basic-pbx/modules.conf (added),
+	  configs/basic-pbx/voicemail.conf (added): configs/basic-pbx -
+	  Super Awesome Company example configs Phase 1, Patch 1 Example
+	  configuration files for a "basic PBX" deployment for the
+	  fictitious Super Awesome Company. Details at
+	  https://reviewboard.asterisk.org/r/4379/ and
+	  https://wiki.asterisk.org/wiki/display/AST/Super+Awesome+Company
+	  Reported by: Malcolm Davenport Tested by: Rusty Newton Review:
+	  https://reviewboard.asterisk.org/r/4379/
+
+2015-02-25 23:09 +0000 [r432258-432281]  Matthew Jordan <mjordan at digium.com>
+
+	* /, configure, configure.ac: configure: Promote SQLite3 "not
+	  installed" warning to error Since Asterisk won't build without
+	  the library, not having it is definitely an error. Thanks to Kyle
+	  Kurz for pointing this out. ........ Merged revisions 432280 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+	* /, channels/chan_sip.c: channels/chan_sip: Clarify WARNING
+	  message in mismatched SRTP scenario When we receive an SDP as
+	  part of an offer/answer for a peer/friend has been configured to
+	  require encryption, and that SDP offer/answer failed to provide
+	  acceptable crypto attributes, we currently issue a WARNING that
+	  uses the phrase "we" and "requested". In this case, both of those
+	  terms are ambiguous - the user will probably think "we" is
+	  Asterisk (it most likely isn't) and it may not be a "request", so
+	  much as an SDP that was received in some fashion. This patch
+	  makes the WARNING messages slightly less bad and a bit more
+	  accurate as well. ASTERISK-23214 #close Reported by: Rusty Newton
+	  ........ Merged revisions 432277 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+	* main/sdp_srtp.c, /: channels/sip/sdp_crypto: Handle SRTP keys
+	  negotiated with key lifetime/MKI Prior to this patch, SDP offers
+	  negotiating SDES-SRTP crypto attributes would be rejected if
+	  those crypto attributes contained either a key lifetime or a MKI
+	  parameter. While from a theoretical point of view this was
+	  defensible - Asterisk does not support key lifetimes or multiple
+	  crypto keys - from a practical point of view, this is quite a
+	  problem. A large number of endpoints offer lifetimes/MKI, which
+	  Asterisk can tolerate so long as it doesn't actually have to
+	  support anything more than a single key or refresh the key. In
+	  reality, this is (so far as we've seen) always the case. This
+	  patch is a forward port of Olle's work in the
+	  lingon-srtp-key-lifetime-1.8 branch. To quote Olle from
+	  ASTERISK-17721, it handles lifetime/MKI parameters in the
+	  following fashion: > The Lingon branch now handle lifetime and
+	  MKI parameters. > > We only accept lifetimes up to max for the
+	  crypto and higher than 10 hours > for packetization of 20 ms (50
+	  pps). > > We only handle MKI with index 1. > > We do not really
+	  bother with counting packets and reinviting at end of > lifetime,
+	  so the min of 10 hours kind of takes care of most calls. If there
+	  > are longer ones, we rely on the other side for re-invites. > >
+	  It's still not perfect, but I personally think this is an
+	  improvement. A > configuration option for minimum lifetime
+	  accepted could be added. When the patch was ported forward, I
+	  decided against adding a configuration option as Olle's handling
+	  was more than sufficient for every case I've seen come through
+	  the issue tracker or through interoperability testing. We can
+	  revisit that decision if it proves to be false. A few small other
+	  tweaks were made to the surrounding code to reduce indentation
+	  and provide better type safety for the 'tag' parameter. Review:
+	  https://reviewboard.asterisk.org/r/4419/ Review:
+	  https://reviewboard.asterisk.org/r/4418/ ASTERISK-17721 #close
+	  Reported by: Terry Wilson ASTERISK-17899 #close Reported by:
+	  Dwayne Hubbard patches: lingon-srtp-key-lifetime-1.8.diff
+	  uploaded by oej (License 5267) ASTERISK-20233 Reported by: tootai
+	  ASTERISK-22748 Reported by: Alejandro Mejia ........ Merged
+	  revisions 432239 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2015-02-25 20:44 +0000 [r432237]  David M. Lee <dlee at digium.com>
+
+	* res/res_http_websocket.c, /: Increase WebSocket frame size and
+	  improve large read handling Some WebSocket applications, like
+	  [chan_respoke][], require a larger frame size than the default
+	  8k; this patch bumps the default to 16k. This patch also fixes
+	  some problems exacerbated by large frames. The sanity counter was
+	  decremented on every fread attempt in ws_safe_read(), regardless
+	  of whether data was read from the socket or not. For large
+	  frames, this could result in loss of sanity prior to reading the
+	  entire frame. (16k frame / 1448 bytes per segment = 12 segments).
+	  This patch changes the sanity counter so that it only decrements
+	  when fread() doesn't read any bytes. This more closely matches
+	  the original intention of ws_safe_read(), given that the error
+	  message is "Websocket seems unresponsive". This patch also
+	  properly logs EOF conditions, so disconnects are no longer
+	  confused with unresponsive connections. [chan_respoke]:
+	  https://github.com/respoke/chan_respoke Review:
+	  https://reviewboard.asterisk.org/r/4431/ ........ Merged
+	  revisions 432236 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2015-02-24 22:14 +0000 [r432195-432199]  Matthew Jordan <mjordan at digium.com>
+
+	* /, channels/chan_sip.c: channels/chan_sip: Fix crash when
+	  transmitting packet after thread shutdown When the monitor thread
+	  is stopped, its pthread ID is set to a specific value
+	  (AST_PTHREADT_STOP) so that later portions of the code can
+	  determine whether or not it is safe to manipulate the thread.
+	  Unfortunately, __sip_reliable_xmit failed to check for that
+	  value, checking instead only for AST_PTHREAD_STOP. Passing the
+	  invalid yet very specific value to pthread_kill causes a crash.
+	  This patch adds a check for AST_PTHREADT_STOP in
+	  __sip_reliable_xmit such that it doesn't attempt to poke the
+	  thread if the thread has already been stopped. ASTERISK-24800
+	  #close Reported by: JoshE ........ Merged revisions 432198 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+	* channels/chan_pjsip.c, main/channel.c, res/res_pjsip_sdp_rtp.c,
+	  res/ari/resource_channels.c: ARI/PJSIP: Apply requesting
+	  channel's format cap to created channels This patch addresses the
+	  following problems: * ari/resource_channels: In ARI, we currently
+	  create a format capability structure of SLIN and apply it to the
+	  new channel being created. This was originally done when the PBX
+	  core was used to create the channel, as there was a condition
+	  where a newly created channel could be created without any
+	  formats. Unfortunately, now that the Dial API is being used, this
+	  has two drawbacks: (a) SLIN, while it will ensure audio will
+	  flows, can cause a lot of needless transcodings to occur,
+	  particularly when a Local channel is created to the dialplan.
+	  When no format capabilities are available, the Dial API handles
+	  this better by handing all audio formats to the requsted
+	  channels. As such, we defer to that API to provide the format
+	  capabilities. (b) If a channel (requester) is causing this
+	  channel to be created, we currently don't use its format
+	  capabilities as we are passing in our own. However, the Dial API
+	  will use the requester channel's formats if none are passed into
+	  it, and the requester channel exists and has format capabilities.
+	  This is the "best" scenario, as it is the most likely to create a
+	  media path that minimizes transcoding. Fixing this simply entails
+	  removing the providing of the format capabilities structure to
+	  the Dial API. * chan_pjsip: Rather than blindly picking the first
+	  format in the format capability structure - which actually *can*
+	  be a video or text format - we select an audio format, and only
+	  pick the first format if that fails. That minimizes the weird
+	  scenario where we attempt to transcode between video/audio. *
+	  res_pjsip_sdp_rtp: Applied the joint capapbilites to the format
+	  structure. Since ast_request already limits us down to one format
+	  capability once the format capabilities are passed along, there's
+	  no reason to squelch it here. * channel: Fixed a comment. The
+	  reason we have to minimize our requested format capabilities down
+	  to a single format is due to Asterisk's inability to convey the
+	  format to be used back "up" a channel chain. Consider the
+	  following: PJSIP/A => L;1 <=> L;2 => PJSIP/B g,u,a g,u,a g,u,a u
+	  That is, we have PJSIP/A dialing a Local channel, where the
+	  Local;2 dials PJSIP/B. PJSIP/A has native format capabilities
+	  g722,ulaw,alaw; the Local channel has inherited those format
+	  capabilities down the line; PJSIP/B supports only ulaw. According
+	  to these format capabilities, ulaw is acceptable and should be
+	  selected across all the channels, and no transcoding should
+	  occur. However, there is no way to convey this: when L;2 and
+	  PJSIP/B are put into a bridge, we will select ulaw, but that is
+	  not conveyed to PJSIP/A and L;1. Thus, we end up with: PJSIP/A
+	  <=> L;1 <=> L;2 <=> PJSIP/B g g X u u Which causes g722 to be
+	  written to PJSIP/B. Even if we can convey the 'ulaw' choice back
+	  up the chain (which through some severe hacking in Local channels
+	  was accomplished), such that the chain looks like: PJSIP/A <=>
+	  L;1 <=> L;2 <=> PJSIP/B u u u u We have no way to tell PJSIP/A's
+	  *channel driver* to Answer in the SDP back with only 'ulaw'. This
+	  results in all the channel structures being set up correctly, but
+	  PJSIP/A *still* sending g722 and causing the chain to fall apart.
+	  There's a lot of difficulty just in setting this up, as there are
+	  numerous race conditions in the act of bridging, and no clean
+	  mechanism to pass the selected format backwards down an
+	  established channel chain. As such, the best that can be done at
+	  this point in time is clarifying the comment. Review:
+	  https://reviewboard.asterisk.org/r/4434/ ASTERISK-24812 #close
+	  Reported by: Matt Jordan
+
+2015-02-24 18:32 +0000 [r432175]  Kevin Harwell <kharwell at digium.com>
+
+	* /, bridges/bridge_softmix.c: bridge_softmix: G.729 codec license
+	  held When more than one call using the same codec type enters
+	  into a softmix bridge and no audio is present for a channel the
+	  bridge optimizes the out frame by using the same one for all
+	  channels with the same codec type. Unfortunately, when that
+	  number (channels with same codec type) dropped to <= 1 the codec
+	  was not dereferenced. At least not until all parties left the
+	  bridge. Thus in the case of G.729 the license was not released.
+	  This patch ensures that the codec is dereferenced immediately
+	  when the optimization no longer applies. ASTERISK-24797 #close
+	  Reported by: Luke Hulsey Review:
+	  https://reviewboard.asterisk.org/r/4429/ ........ Merged
+	  revisions 432174 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2015-02-21 20:47 +0000 [r432118-432154]  Joshua Colp <jcolp at digium.com>
+
+	* rest-api/api-docs/channels.json, res/ari/resource_channels.c,
+	  res/res_ari_channels.c: res_ari_channels: Return a 404 response
+	  when a requested channel variable does not exist. This change
+	  makes it so that if a channel variable is requested and it does
+	  not exist a 404 response will be returned instead of an
+	  allocation failed response. This makes it easier to debug and
+	  figure out what is going on for a user. ASTERISK-24677 #close
+	  Reported by: Joshua Colp
+
+	* res/res_pjsip_registrar.c: res_pjsip_registrar: Add Expires
+	  header to 200 OK if present in REGISTER. Some implementations
+	  don't pay attention to the expires for individual contacts. In
+	  this case they may consider the lack of an Expires header in the
+	  200 OK as unregistered. This change makes it so if an Expires
+	  header is present in the REGISTER we will add one in the 200 OK.
+	  ASTERISK-24785 #close Reported by: Ross Beer
+
+	* res/res_pjsip.c: res_pjsip: Add a log message when creating a UAC
+	  dialog to a target URI that is invalid. ASTERISK-24499 #close
+	  Reported by: Rusty Newton
+
+2015-02-21 17:35 +0000 [r432099]  Matthew Jordan <mjordan at digium.com>
+
+	* apps/app_voicemail.c, /: apps/app_voicemail: Demote an ERROR
+	  message to a WARNING message When using IMAP voicemail with
+	  FreePBX, you will often get ERROR messages complaining about not
+	  being able to find a mailbox. This is due to how FreePBX handles
+	  voicemail mailboxes. Unfortunately, app_voicemail has to consider
+	  this a configuration error, as in any other system it would be
+	  indicative of someone misconfiguring their system. Regardless, a
+	  misconfiguration is a WARNING, and not an ERROR. This patch
+	  demotes the message so that system administrators can hopefully
+	  reduce some of the noise in their log files. Note that in the
+	  original patch this was made into a NOTICE, but that's a too
+	  forgiving. ASTERISK-24790 #close Reported by: Graham Barnett
+	  patches: app_voicemail.c.patch_noise uploaded by Graham Barnett
+	  (License 6685) ........ Merged revisions 432098 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2015-02-21 14:05 +0000 [r432079]  Joshua Colp <jcolp at digium.com>
+
+	* main/http.c, /: http: Add missing html tag to 'httpstatus'
+	  functionality. ASTERISK-24724 #close Reported by: Ashley Sanders
+	  ........ Merged revisions 432078 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2015-02-21 02:56 +0000 [r432055-432059]  Corey Farrell <git at cfware.com>
+
+	* /, main/bucket.c, main/codec.c, main/loader.c: Allow shutdown to
+	  unload modules that register bucket scheme's or codec's. * Change
+	  __ast_module_shutdown_ref to be NULL safe (11+). * Allow modules
+	  that call ast_bucket_scheme_register or ast_codec_register to be
+	  unloaded during graceful shutdown only (13+ only). ASTERISK-24796
+	  #close Reported by: Corey Farrell Review:
+	  https://reviewboard.asterisk.org/r/4428/ ........ Merged
+	  revisions 432058 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+	* /, include/asterisk/lock.h: asterisk/lock.h: Fix syntax errors
+	  for non-gcc OSX with 64-bit integers. Add a couple of missing
+	  closing brackets / parenthesis. ASTERISK-24814 #close Reported
+	  by: Corey Farrell Review:
+	  https://reviewboard.asterisk.org/r/4436/ ........ Merged
+	  revisions 432054 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2015-02-20 17:51 +0000 [r432034]  Richard Mudgett <rmudgett at digium.com>
+
+	* /, channels/sig_analog.c: chan_dahdi/sig_analog: Put log message
+	  strings on one line. With the log messages on one line, you can
+	  search for the log message seen in the log and expect to find it.
+	  ........ Merged revisions 432032 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2015-02-20 17:46 +0000 [r432033]  George Joseph <george.joseph at fairview5.com>
+
+	* res/res_pjsip_publish_asterisk.c, res/res_pjsip_acl.c:
+	  ASTERISK-24811: Add ast_sorcery_apply_config() to
+	  res_pjsip_publish_asterisk. Matt Hoskins reported that
+	  res_pjsip_publish_asterisk wouldn't pull config from realtime.
+	  Turns out it was just missing a call ast_sorcery_apply_config().
+	  res_pjsip_acl was missing it as well, so I added it. The other
+	  pjsip modules looked OK. ASTERISK-24811 #close Reported-by: Matt
+	  Hoskins Tested-by: George Joseph Tested-by: Matt Hoskins patches:
+	  res_pjsip_publish_asterisk.c.patch submitted by Matt Hoskins
+	  (license 6688) Review: https://reviewboard.asterisk.org/r/4433/
+
+2015-02-20 15:47 +0000 [r432013]  Matthew Jordan <mjordan at digium.com>
+
+	* apps/app_voicemail.c, /: apps/app_voicemail: Fix IMAP header
+	  compatibility issue with Microsoft Exchange When interfacing with
+	  Microsoft Exchange, custom headers will be returned as all lower
+	  case. Currently, the IMAP header code will fail to parse the
+	  returned custom headers, as it will be performing a case
+	  sensitive comparison. This can cause playback of messages to
+	  fail, as needed information - such as origtime - will not be
+	  present. This patch updates app_voicemail's header parsing code
+	  to perform a case insensitive lookup for the requested custom
+	  headers. Since the headers are specific to Asterisk, e.g.,
+	  'x-asterisk-vm-orig-time', and headers should be unique in an
+	  IMAP message, this should cause no issues with other systems.
+	  ASTERISK-24787 #close Reported by: Graham Barnett patches:
+	  app_voicemail.c.patch_MSExchange uploaded by Graham Barnett
+	  (License 6685) ........ Merged revisions 432012 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2015-02-19 21:25 +0000 [r431956-431993]  Richard Mudgett <rmudgett at digium.com>
+
+	* channels/chan_dahdi.c, channels/sig_analog.c, /: chan_dahdi:
+	  Remove some dead code. ........ Merged revisions 431992 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+	* main/aoc.c: ISDN AOC: Fix crash from an AOC-E message that
+	  doesn't have a channel association. Processing an AOC-E event
+	  that does not or no longer has a channel association causes a
+	  crash. The problem with posting AOC events to the channel topic
+	  is that AOC-E events don't always have a channel association and
+	  posting the event to the all channels topic is just wrong. AOC-E
+	  events do however have their own charging association method to
+	  refer to the agreement with the charging entity. * Changed the
+	  AOC events to post to the AMI manager topic instead of the
+	  channel topics. If a channel is associated with the event then
+	  channel snapshot information is supplied with the AMI event. *
+	  Eliminated RAII_VAR() usage in aoc_to_ami() and
+	  ast_aoc_manager_event(). This patch supercedes the patch on
+	  Review: https://reviewboard.asterisk.org/r/4427/ ASTERISK-22670
+	  #close Reported by: klaus3000 ASTERISK-24689 #close Reported by:
+	  Marcel Manz ASTERISK-24740 #close Reported by: Panos Gkikakis
+	  Review: https://reviewboard.asterisk.org/r/4430/
+
+	* res/res_pjsip_refer.c: res_pjsip_refer: Handle INVITE with
+	  Replaces failure after answer. * Fixed hangup handling of the
+	  session->channel after answer if the ast_channel_move() or
+	  ast_bridge_impart() fails. We are still the thread controlling
+	  the session->channel so we need to call ast_hangup() to kill the
+	  channel. * Fixed debug messages in
+	  refer_incoming_invite_request() referencing incorrect channnels
+	  on success. Code comments now say why the session->channel cannot
+	  be used. Review: https://reviewboard.asterisk.org/r/4422/
+
+2015-02-19 15:28 +0000 [r431937]  Matthew Jordan <mjordan at digium.com>
+
+	* main/tcptls.c, /: tcptls: Handle new OpenSSL compile time option
+	  to disable SSLv3 Some distributions are going to disable SSLv3 at
+	  compile time. This option can be checked using the directive
+	  OPENSSL_NO_SSL3_METHOD. This patch updates the TCP/TLS handling
+	  in Asterisk to look for that directive before attempting to use
+	  the SSLv3 specific methods. ASTERISK-24799 #close Reported by:
+	  Alexander Traud patches: no-ssl3-method.patch uploaded by
+	  Alexander Traud (License 6520) ........ Merged revisions 431936
+	  from http://svn.asterisk.org/svn/asterisk/branches/11
+
+2015-02-19 02:01 +0000 [r431917]  Corey Farrell <git at cfware.com>
+
+	* main/sched.c, /, include/asterisk/sched.h, channels/chan_iax2.c:
+	  Create work around for scheduler leaks during shutdown. * Added
+	  ast_sched_clean_by_callback for cleanup of scheduled events that
+	  have not yet fired. * Run all pending peercnt_remove_cb and
+	  replace_callno events in chan_iax2. Cleanup of replace_callno
+	  events is only run 11, since it no longer releases any references
+	  or allocations in 13+. ASTERISK-24451 #close Reported by: Corey
+	  Farrell Review: https://reviewboard.asterisk.org/r/4425/ ........
+	  Merged revisions 431916 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2015-02-17 15:31 +0000 [r431898]  Richard Mudgett <rmudgett at digium.com>
+
+	* res/res_pjsip_sdp_rtp.c, res/res_pjsip_messaging.c,
+	  res/res_pjsip_caller_id.c, res/res_pjsip_refer.c,
+	  res/res_pjsip_send_to_voicemail.c: res_pjsip_refer: Fix crash
+	  from a REFER and BYE collision. Analyzing a one-off crash on a
+	  busy system showed that processing a REFER request had a NULL
+	  session channel pointer. The only way I can think of that could
+	  cause this is if an outgoing BYE transaction overlapped the
+	  incoming REFER transaction in a collision. Asterisk sends a BYE
+	  while the phone sends a REFER to complete an attended transfer. *
+	  Made check the session channel pointer before processing an
+	  incoming REFER request in res_pjsip_refer. * Fixed similar crash
+	  potential for res_pjsip supplement incoming request processing
+	  for res_pjsip_sdp_rtp INFO, res_pjsip_caller_id INVITE/UPDATE,
+	  res_pjsip_messaging MESSAGE, and res_pjsip_send_to_voicemail
+	  REFER messages. * Made res_pjsip_messaging respond to a message
+	  body too large with a 413 instead of ignoring it. ASTERISK-24700
+	  #close Reported by: Zane Conkle Review:
+	  https://reviewboard.asterisk.org/r/4417/
+
+2015-02-16 21:29 +0000 [r431879]  Matthew Jordan <mjordan at digium.com>
+
+	* res/res_rtp_asterisk.c: res/res_rtp_asterisk: Fix crash in debug
+	  from RTCP reports without report block When RTCP debugging was
+	  enabled, an RTCP report without a report block would cause a
+	  crash. This was due to the verbose output not checking to see if
+	  the report_block pointer was NULl before dereferencing it. This
+	  patch adds the necessary check to prevent printing any verbose
+	  output if the far side hasn't provided us the information they
+	  should have. ASTERISK-24791 #close Reported by: JoshE Tested by:
+	  JoshE
+
+2015-02-15 19:00 +0000 [r431807-431860]  Joshua Colp <jcolp at digium.com>
+
+	* configs/samples/pjsip.conf.sample: pjsip: Remove "contact" type
+	  from pjsip.conf.sample The "contact" object is not meant to be
+	  configured from the pjsip.conf configuration file. It is meant to
+	  be created as a result of a registration and stored elsewhere.
+	  ASTERISK-24085 #close Reported by: Rusty Newton
+
+	* contrib/scripts/install_prereq: install_prereq: Tweak flags when
+	  configuring pjproject. This change does two things: 1. Disables
+	  debugging so assertions which can return an error do, instead of
+	  asserting. 2. Enables IPv6 support. ASTERISK-24632 #close
+	  Reported by: Rusty Newton
+
+	* res/res_sorcery_config.c: res_sorcery_config: Improve object
+	  lookup times. The res_sorcery_config module currently uses a
+	  fixed bucket size of 53. This means that depending on the number
+	  of objects you either end up with excess buckets or a lot of
+	  collisions. Due to the way that res_sorcery_config is implemented
+	  it's actually possible to make the bucket size dynamic based on
+	  the number of objects. This is due to the fact that each loading
+	  of the config file produces a new container and does not modify
+	  the existing one. This change uses the number of expected objects
+	  and finds a prime number near it. In practice depending on the
+	  number of objects this can speed up lookups anywhere from 2X to
+	  15X. This change also removes the lock from the container as it
+	  is not needed. Review: https://reviewboard.asterisk.org/r/4423/
+
+	* res/res_pjsip/pjsip_cli.c: res_pjsip: Add "pjsip show version"
+	  CLI command. When debugging things it can be useful to know
+	  absolutely what version of pjproject res_pjsip is running
+	  against. This change adds a "pjsip show version" CLI command
+	  which can be used to query for this. ASTERISK-24685 #close
+	  Reported by: Joshua Colp Review:
+	  https://reviewboard.asterisk.org/r/4424/
+
+	* res/res_timing_pthread.c: res_timing_pthread: Fix leaky pipes.
+	  During some refactoring the way private information for timers
+	  was stored was changed. As a result of this the action which
+	  normally removed the timer upon closure in res_timing_pthread was
+	  also removed causing the timer to remain after it should using up
+	  resources. This change ensures that the timer is removed upon
+	  closure. ASTERISK-24768 #close Reported by: Matthias Urlichs
+	  patches: timer.patch submitted by Matthias Urlichs (license 5508)
+
+2015-02-15 00:32 +0000 [r431789]  Matthew Jordan <mjordan at digium.com>
+
+	* /, apps/app_mixmonitor.c: apps/app_mixmonitor: Move Test Event
+	  for MIXMONITOR_END to after it finishes The Test Event for
+	  MIXMONITOR_END - which signals that a MixMonitor has completed -
+	  technically fired before the filestream was closed. If a test
+	  used this to trigger a condition to verify that the file was
+	  written, it could result in a race condition where the file size
+	  would not be what the test expected. Luckily, no tests were using
+	  this (although they should have been). Since the test event
+	  needed to be moved after the point where the MixMonitor autochan
+	  has been destroyed, the test event no longer emits the channel
+	  name. Luckily, nothing needs it. ........ Merged revisions 431788
+	  from http://svn.asterisk.org/svn/asterisk/branches/11
+
+2015-02-14 19:45 +0000 [r431751-431771]  Joshua Colp <jcolp at digium.com>
+
+	* main/sorcery.c: sorcery: Output an error message if a wizard is
+	  specified for an object type and it isn't found. ASTERISK-24612
+	  #close Reported by: Joshua Colp
+
+	* res/res_pjsip_exten_state.c: res_pjsip_exten_state: Improve log
+	  message when a subscription is attempted to a non-existent
+	  extension. ASTERISK-24716 #close Reported by: Rusty Newton
+
+	* channels/pjsip/dialplan_functions.c: 'information' ends with an
+	  'n'.
+
+	* channels/pjsip/dialplan_functions.c: chan_pjsip: Fix crash when
+	  CHANNEL dialplan function is invoked with pjsip argument and no
+	  type. ASTERISK-24771 #close Reported by: Niklas Larsson
+
+2015-02-13 17:21 +0000 [r431734]  Richard Mudgett <rmudgett at digium.com>
+
+	* res/res_pjsip_session.c: res_pjsip_session: Fix double re-INVITE
+	  collision crash. A multi-asterisk box setup with direct media
+	  enabled would occasionally crash when two re-INVITE collisions on
+	  a call leg happen in a row. The re-INVITE logic only had one
+	  timer struct to defer the re-INVITE. When the second collision
+	  happens the timer struct is overwritten and put into the timer
+	  heap again. Resources for the first timer are leaked and the heap
+	  has two positions occupied by the same timer struct. Now the heap
+	  ordering is potentially corrupted, the timer will fire twice, and
+	  any resources allocated for the second timer will be released
+	  twice. * The solution is to put the collided re-INVITE into the
+	  delayed requests queue with all the other delayed requests and
+	  cherry pick the next request that can come off the queue when an
+	  event happens. * Changed to put delayed BYE requests at the head
+	  of the delayed queue. There is no sense in processing delayed
+	  UPDATEs and re-INVITEs when a BYE has been requested. * Made the
+	  start of a BYE request flush the delayed requests queue to
+	  prevent a delayed request from overlapping the BYE transaction. I
+	  saw a few cases where a delayed re-INVITE got started after the
+	  BYE transaction started. * Changed the delayed_request struct to
+	  use an enum instead of a string for the request method. Cherry
+	  picking the queue is easier with an enum than string comparisons
+	  and the compiler can warn if a switch statement does not cover
+	  all defined enum values. * Improved the debug output to give more
+	  information. It helps to know which channel is involved with an
+	  endpoint. Trunks can have many channels associated with the
+	  endpoint at the same time. ASTERISK-24727 #close Reported by:
+	  Mark Michelson Review: https://reviewboard.asterisk.org/r/4414/
+
+2015-02-12 20:32 +0000 [r431717]  Matthew Jordan <mjordan at digium.com>
+
+	* res/res_pjsip_multihomed.c, res/stasis/control.c,
+	  include/asterisk/stasis_app.h, rest-api/api-docs/channels.json,
+	  res/ari/resource_channels.c, CHANGES, res/res_ari_channels.c,
+	  channels/chan_pjsip.c, res/res_pjsip_nat.c,
+	  res/res_pjsip_transport_websocket.c, res/ari/resource_channels.h:
+	  ARI/PJSIP: Add the ability to redirect (transfer) a channel in a
+	  Stasis app This patch adds a new feature to ARI to redirect a
+	  channel to another server, and fixes a few bugs in PJSIP's
+	  handling of the Transfer dialplan application/ARI redirect
+	  capability. *New Feature* A new operation has been added to the
+	  ARI channels resource, redirect. With this, a channel in a Stasis
+	  application can be redirected to another endpoint of the same
+	  underlying channel technology. *Bug fixes* In the process of
+	  writing this new feature, two bugs were fixed in the PJSIP stack:
+	  (1) The existing .transfer channel callback had the limitation
+	  that it could only transfer channels to a SIP URI, i.e., you had
+	  to pass 'PJSIP/sip:foo at my_provider.com' to the dialplan
+	  application. While this is still supported, it is somewhat
+	  unintuitive - particularly in a world full of endpoints. As such,
+	  we now also support specifying the PJSIP endpoint to transfer to.
+	  (2) res_pjsip_multihomed was, unfortunately, trying to 'help' a
+	  302 redirect by updating its Contact header. Alas, that resulted
+	  in the forwarding destination set by the dialplan application/ARI
+	  resource/whatever being rewritten with very incorrect
+	  information. Hence, we now don't bother updating an outgoing
+	  response if it is a 302. Since this took a looong time to find,
+	  some additional debug statements have been added to those modules
+	  that update the Contact headers. Review:
+	  https://reviewboard.asterisk.org/r/4316/ ASTERISK-24015 #close
+	  Reported by: Private Name ASTERISK-24703 #close Reported by: Matt
+	  Jordan
+
+2015-02-11 18:02 +0000 [r431693-431698]  Kevin Harwell <kharwell at digium.com>
+
+	* res/res_pjsip/pjsip_configuration.c: res_pjsip: dtls_handler
+	  causes Asterisk to crash There have been a couple of times where
+	  a crash occurred in the dtls_handler section of the code for
+	  res_pjsip. Unfortunately, in working this issue the problem was
+	  unable to be reproduced. After looking at the backtraces and
+	  through the code the current best guess as to why this happened
+	  might be due to a reentrance problem and the strtok function. So,
+	  the current fix is to convert the strtok function into the
+	  reentrant version of the function, strtok_r. ASTERISK-24741
+	  #close Reported by: Zane Conkle Review:
+	  https://reviewboard.asterisk.org/r/4409/
+
+	* res/ari/ari_websockets.c: ari_websockets: removed extra check on
+	  websocket session read When merging the websocket timeout issue
+	  (ASTERISK-24701) an extra, almost duplicate, check was left in
+	  the code that should not have been. This removes it.
+	  ASTERISK-24701 #close Reported by: Matt Jordan Review:
+	  https://reviewboard.asterisk.org/r/4412/
+
+2015-02-11 17:28 +0000 [r431692]  Richard Mudgett <rmudgett at digium.com>
+
+	* main/bridge.c, main/http.c, apps/app_confbridge.c,
+	  include/asterisk/channel.h, res/res_pjsip/pjsip_options.c,
+	  res/res_pjsip_pubsub.c, main/asterisk.c, main/channel.c,
+	  include/asterisk.h, channels/chan_sip.c: HTTP: Stop accepting
+	  requests on final system shutdown. There are three CLI commands
+	  to stop and restart Asterisk each. 1) core stop/restart now -
+	  Hangup all calls and stop or restart Asterisk. New channels are
+	  prevented while the shutdown request is pending. 2) core
+	  stop/restart gracefully - Stop or restart Asterisk when there are
+	  no calls remaining in the system. New channels are prevented
+	  while the shutdown request is pending. 3) core stop/restart when
+	  convenient - Stop or restart Asterisk when there are no calls in
+	  the system. New calls are not prevented while the shutdown
+	  request is pending. ARI has made stopping/restarting Asterisk
+	  more problematic. While a shutdown request is pending it is
+	  desirable to continue to process ARI HTTP requests for current
+	  calls. To handle the current calls while a shutdown request is
+	  pending, a new committed to shutdown phase is needed so ARI
+	  applications can deal with the calls until the system is fully
+	  committed to shutdown. * Added a new shutdown committed phase so
+	  ARI applications can deal with calls until the final committed to
+	  shutdown phase is reached. * Made refuse new HTTP requests when
+	  the system has reached the final system shutdown phase. Starting
+	  anything while the system is actively releasing resources and
+	  unloading modules is not a good thing. * Split the bridging
+	  framework shutdown to not cleanup the global bridging containers
+	  when shutting down in a hurry. This is similar to how other
+	  modules prevent crashes on rapid system shutdown. * Moved
+	  ast_begin_shutdown(), ast_cancel_shutdown(), and
+	  ast_shutting_down(). You should not have to include channel.h
+	  just to access these system functions. ASTERISK-24752 #close
+	  Reported by: Matthew Jordan Review:
+	  https://reviewboard.asterisk.org/r/4399/
+
+2015-02-11 17:12 +0000 [r431674]  Matthew Jordan <mjordan at digium.com>
+
+	* /, channels/chan_sip.c: channels/chan_sip: Fix RealTime error
+	  during SIP unregistration with MariaDB When a SIP device that has
+	  its registration stored in RealTime unregisters, the entry for
+	  that device is updated with blank values, i.e., "", indicating
+	  that it is no longer registered. Unfortunately, one of those
+	  values that is 'blanked' is the device's port. If the column type
+	  for the port is not a string datatype (the recommended type is
+	  integer), an ODBC or database error will be thrown. MariaDB does
+	  not coerce empty strings to a valid integer value. This patch
+	  updates the query run from chan_sip such that it replaces the
+	  port value with a value of '0', as opposed to a blank value. This
+	  is the value that other database backends coerce the empty string
+	  ("") to already, and the handling of reading a RealTime
+	  registration value from a backend already anticipates receiving a
+	  port of '0' from the backends. ASTERISK-24772 #close Reported by:
+	  Richard Miller patches: chan_sip.diff uploaded by Richard Miller
+	  (License 5685) ........ Merged revisions 431673 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2015-02-11 16:51 +0000 [r431670]  Kevin Harwell <kharwell at digium.com>
+
+	* res/res_http_websocket.c, res/ari/ari_websockets.c, /:
+	  res_http_websocket: websocket write timeout fails to fully
+	  disconnect When writing to a websocket if a timeout occurred the
+	  underlying socket did not get closed/disconnected. This patch
+	  makes sure the websocket gets disconnected on a write timeout.
+	  Also a notice is logged stating that the websocket was
+	  disconnected. ASTERISK-24701 #close Reported by: Matt Jordan
+	  Review: https://reviewboard.asterisk.org/r/4412/ ........ Merged
+	  revisions 431669 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2015-02-11 15:51 +0000 [r431663]  Corey Farrell <git at cfware.com>
+
+	* include/asterisk/module.h, main/loader.c, /,
+	  bridges/bridge_builtin_features.c: Enable REF_DEBUG for
+	  ast_module_ref / ast_module_unref. Add ast_module_shutdown_ref
+	  for use by modules that can only be unloaded during graceful
+	  shutdown. When REF_DEBUG is enabled: * Add an empty ao2 object to
+	  struct ast_module. * Allocate ao2 object when the module is
+	  loaded. * Perform an ao2_ref in each place where mod->usecount is
+	  manipulated. * ao2_cleanup on module unload. ASTERISK-24479
+	  #close Reported by: Corey Farrell Review:
+	  https://reviewboard.asterisk.org/r/4141/ ........ Merged
+	  revisions 431662 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2015-02-10 23:16 +0000 [r431643]  George Joseph <george.joseph at fairview5.com>
+
+	* res/res_pjsip_config_wizard.c,
+	  configs/samples/pjsip_wizard.conf.sample:
+	  res_pjsip_config_wizard: Add ability to auto-create hints.
+	  Looking at the Super Awesome Company sample reminded me that
+	  creating hints is just plain gruntwork. So you can now have the
+	  pjsip conifg wizard auto-create them for you. Specifying
+	  'hint_exten' in the wizard will create 'exten =>
+	  <hint_exten>,hint/PJSIP/<wizard_id>' in whatever is specified for
+	  'hint_context'. Specifying 'hint_application' in the wizard will
+	  create 'exten => <hint_exten>,1,<hint_application>' in whatever
+	  is specified for 'hint_context'. The default for 'hint_context'
+	  is the endpoint's context. There's no default for
+	  'hint_application'. If not specified, no app is added. There's no
+	  default for 'hint_exten'. If not specified, neither the hint
+	  itself nor the application will be created. Some may think this
+	  is the slippery slope to users.conf but hints are a basic
+	  necessity for phones unlike voicemail, manager, etc that
+	  users.conf creates. Tested-by: George Joseph Review:
+	  https://reviewboard.asterisk.org/r/4383/
+
+2015-02-09 03:10 +0000 [r431600-431622]  Matthew Jordan <mjordan at digium.com>
+
+	* rest-api/api-docs/channels.json, res/ari/resource_channels.c:
+	  res/ari/resource_channels: Add missing 'no_answer' reason to
+	  DELETE /channels One of the canonical reasons for hanging up a
+	  channel is because the far end failed to answer - or because
+	  someone else answered, and we want to get rid of this channel.
+	  This patch adds the missing value to the 'reason' query parameter
+	  for the DELETE /channels operation. Review:
+	  https://reviewboard.asterisk.org/r/4400 ASTERISK-24745 #close
+	  Reported by: Ben Merrills patches:
+	  add_no_answer_ari_hangup_cause.diff uploaded by Ben Merrills
+	  (License 6678)
+
+	* /, res/res_odbc.c: res/res_odbc: Remove unneeded queries when
+	  determining if a table exists This patch modifies the
+	  ast_odbc_find_table function such that it only performs a lookup
+	  of the requested table if the table is not already known. Prior
+	  to this patch, a queries would be executed against the database
+	  even if the table was already known and cached. Review:
+	  https://reviewboard.asterisk.org/r/4405/ ASTERISK-24742 #close
+	  Reported by: ibercom patches: patch.diff uploaded by ibercom
+	  (License 6599) ........ Merged revisions 431617 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+	* res/res_pjsip_sdp_rtp.c: res/res_pjsip_sdp_rtp: Fix leak of local
+	  ICE candidates when applying to SDP When an SDP is created for an
+	  outgoing request/response, the ICE candidates obtained from the
+	  RTP instance are currently leaked. This causes the ao2 container
+	  that holds the candidates to never properly be reclaimed when the
+	  RTP instance is destroyed. This patch properly decrements the ICE
+	  candidates' container if it is successfully obtained.
+	  ASTERISK-24769 #close Reported by: Matt Jordan
+
+2015-02-06 21:26 +0000 [r431583]  Scott Griepentrog <sgriepentrog at digium.com>
+
+	* main/utils.c, res/res_pjsip.c, main/config.c: various: cleanup
+	  issues found during leak hunt In this collection of small patches
+	  to prevent Valgrind errors are: fixes for reference leaks in
+	  config hooks, evaluating a parameter beyond bounds, and accessing
+	  a structure after a lock where it could have been already free'd.
+	  Review: https://reviewboard.asterisk.org/r/4407/
+
+2015-02-04 01:27 +0000 [r431538-431555]  Joshua Colp <jcolp at digium.com>
+
+	* res/res_pjsip_keepalive.c: res_pjsip_keepalive: Don't crash if
+	  PJSIP module is not loaded.
+
+	* main/sorcery.c: sorcery: Don't try to load object types which
+	  haven't been defined. The act of defining wizards for an object
+	  type in sorcery.conf will create a minimal object type. This can
+	  cause a problem when a module has multiple sorcery instances
+	  (which all get the wizards from sorcery.conf applied) but the
+	  sorcery instances do not all contain full information about the
+	  object types. Upon loading errors will occur stating that the
+	  objects can not be created. This is confusing and is actually
+	  perfectly fine. This change makes it so that only object types
+	  which have been fully defined will be loaded. ASTERISK-24748
+	  #close Reported by: Joshua Colp
+
+2015-01-31 16:27 +0000 [r431521]  Joshua Colp <jcolp at digium.com>
+
+	* res/res_format_attr_h264.c: res_format_attr_h264: Fix crash when
+	  determining joint capability. The res_format_attr_h264 module
+	  currently incorrectly attempts to copy SPS and PPS information
+	  from the wrong attribute. This change fixes that. ASTERISK-24616
+	  #close Reported by: Yura Kocyuba Review:
+	  https://reviewboard.asterisk.org/r/4392/
+
+2015-02-06  Asterisk Development Team <asteriskteam at digium.com>
+
+	* Asterisk 13.2.0 Released.
+
+2015-01-30  Asterisk Development Team <asteriskteam at digium.com>
+
+	* Asterisk 13.2.0-rc1 Released.
+
+2015-01-30 17:44 +0000 [r431492]  Richard Mudgett <rmudgett at digium.com>
+
+	* apps/app_agent_pool.c: app_agent_pool: Fix initial module load
+	  agent device state reporting. When the app_agent_pool module
+	  initially loads there is a race condition between the thread
+	  loading agents.conf and the device state internal processing
+	  thread. If the device state internal processing thread handles
+	  the agent creation state updates before the thread that loaded
+	  agents.conf registers the device state provider callback then the
+	  cached agent state is "Invalid". When a consumer module like
+	  app_queue asks for the agent state it gets the cached "Invalid"
+	  state instead of the real state from the provider. * Moved
+	  loading the agents.conf configuration to the last thing setup by
+	  app_agent_pool in load_module(). Now the device state provider
+	  callback is registered before the config is loaded so the agent
+	  creation state updates are guaranteed to get the initial device
+	  state. * Removed some now redundant config cleanup on error in
+	  load_config(). * Added lock protection when accessing the device
+	  state in agent_pvt_devstate_get() and eliminated the RAII_VAR()
+	  usage. ASTERISK-24737 #close Reported by: Steve Pitts Review:
+	  https://reviewboard.asterisk.org/r/4390/
+
+2015-01-30 17:38 +0000 [r431490]  Kevin Harwell <kharwell at digium.com>
+
+	* res/res_pjsip_outbound_publish.c: res_pjsip_outbound_publish:
+	  eventually crashes when no response is ever received When
+	  Asterisk attempts to send SIP outbound publish information and no
+	  response is ever received (no 200 okay, 412, 423) the system
+	  eventually crashes. A response is never received because the
+	  system Asterisk is attempting to send publish information to is
+	  not available. The underlying pjsip framework attempts to send
+	  publish information. After several attempts it calls back into
+	  the Asterisk outbound publish code. At this point if the
+	  "client->queue" is empty Asterisk attempts to schedule a refresh
+	  which utilizes "rdata" and since no response was received the
+	  given "rdata" struture is NULL. Attempting to dereference a NULL
+	  object of course results in a crash. The fix here removes the
+	  dependency on rdata for schedule_publish_refresh. Instead
+	  param->expiration is now passed to it as this is set to -1 if no
+	  response is received. Also added a notification when no response
+	  is received. ASTERISK-24635 #close Reported by: Marco Paland
+	  Review: https://reviewboard.asterisk.org/r/4384/
+
+2015-01-30 16:52 +0000 [r431471]  asanders <asanders at localhost>:
+
+	* include/asterisk/http.h, configs/samples/http.conf.sample,
+	  main/http.c: HTTP: For httpd server, need option to define server
+	  name for security purposes Added a new config property
+	  [servername] to the http.conf file; updated the http server to
+	  use the new property when sending responses, for showing http
+	  status through the CLI and when reporting status through the
+	  'httpstatus' webpage. ASTERISK-24316 #close Reported By: Andrew
+	  Nagy Review: https://reviewboard.asterisk.org/r/4374/
+
+2015-01-30 16:47 +0000 [r431468]  Mark Michelson <mmichelson at digium.com>
+
+	* main/stasis_channels.c, channels/chan_pjsip.c, main/xmldoc.c,
+	  res/res_pjsip_refer.c, main/pbx.c, main/manager.c,
+	  pbx/pbx_spool.c, main/bridge_after.c: Fix some memory leaks.
+	  These memory leaks were found and fixed by John Hardin. I'm just
+	  committing them for him. ASTERISK-24736 #close Reported by Mark
+	  Michelson Review: https://reviewboard.asterisk.org/r/4389
+
+2015-01-29 23:02 +0000 [r431450]  Scott Griepentrog <sgriepentrog at digium.com>
+
+	* include/asterisk/bridge.h, main/bridge.c,
+	  res/stasis/stasis_bridge.c: stasis transfer: fix stasis bridge
+	  push race part two When swapping a Local channel in place of one
+	  already in a bridge (to complete a bridge attended transfer), the
+	  channel that was swapped out can actually be hung up before the
+	  stasis bridge push callback executes on the independant transfer
+	  thread. This results in the stasis app loop dropping out and
+	  removing the control that has the the app name which the local
+	  replacement channel needs so it can re-enter stasis. To avoid
+	  this race condition a new push_peek callback has been added, and
+	  called from the ast_bridge_impart thread before it launches the
+	  independant thread that will complete the transfer. Now the
+	  stasis push_peek callback can copy the stasis app name before the
+	  swap channel can hang up. ASTERISK-24649 Review:
+	  https://reviewboard.asterisk.org/r/4382/
+
+2015-01-29 20:58 +0000 [r431420-431426]  Mark Michelson <mmichelson at digium.com>
+
+	* res/res_pjsip.c, res/res_pjsip_sips_contact.c (added): Use SIPS
+	  URIs in Contact headers when appropriate. RFC 3261 sections
+	  8.1.1.8 and 12.1.1 dictate specific scenarios when we are
+	  required to use SIPS URIs in Contact headers. Asterisk's
+	  non-compliance with this could actually cause calls to get
+	  dropped when communicating with clients that are strict about
+	  checking the Contact header. Both of the SIP stacks in Asterisk
+	  suffered from this issue. This changeset corrects the behavior in
+	  res_pjsip/chan_pjsip.c Review:
+	  https://reviewboard.asterisk.org/r/4345
+
+	* /, channels/chan_sip.c: Use SIPS URIs in Contact headers when
+	  appropriate. RFC 3261 sections 8.1.1.8 and 12.1.1 dictate
+	  specific scenarios when we are required to use SIPS URIs in
+	  Contact headers. Asterisk's non-compliance with this could
+	  actually cause calls to get dropped when communicating with
+	  clients that are strict about checking the Contact header. Both
+	  of the SIP stacks in Asterisk suffered from this issue. This
+	  changeset corrects the behavior in chan_sip. ASTERISK-24646
+	  #close Reported by Stephan Eisvogel Review:
+	  https://reviewboard.asterisk.org/r/4346 ........ Merged revisions
+	  431423 from http://svn.asterisk.org/svn/asterisk/branches/11
+
+	* res/res_pjsip/pjsip_configuration.c: Allow disabling of 100rel
+	  support on PJSIP endpoints. Due to an inversion error, setting
+	  100rel=no would not actually change the current value of the
+	  setting (which defaulted to "yes"). With this fix, the inversion
+	  is corrected.
+
+2015-01-29 16:46 +0000 [r431403]  George Joseph <george.joseph at fairview5.com>
+
+	* res/res_pjsip_exten_state.c: res_pjsip_exten_state: Reduce log
+	  clutter... change a WARNING to a VERBOSE/2 Reduce log clutter by
+	  changing the "Watcher for hint %s (removed|deactivated)" message
+	  from WARNING to VERBOSE/2. Tested-by: George Joseph Review:
+	  https://reviewboard.asterisk.org/r/4387/
+
+2015-01-29 12:09 +0000 [r431385]  Joshua Colp <jcolp at digium.com>
+
+	* res/res_rtp_asterisk.c, /: res_rtp_asterisk: Fix DTLS when used
+	  with OpenSSL 1.0.1k A recent security fix for OpenSSL broke DTLS
+	  negotiation for many applications. This was caused by read ahead
+	  not being enabled when it should be. While a commit has gone into
+	  OpenSSL to force read ahead on for DTLS it may take some time for
+	  a release to be made and the change to be present in
+	  distributions (if at all). As enabling read ahead is a simple one
+	  line change this commit does that and fixes the issue.
+	  ASTERISK-24711 #close Reported by: Jared Biel ........ Merged
+	  revisions 431384 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2015-01-28 17:37 +0000 [r431301-431303]  Mark Michelson <mmichelson at digium.com>
+
+	* /, res/res_pjsip_sdp_rtp.c, res/res_pjsip_t38.c,
+	  res/res_pjsip_session.c: Fix file descriptor leak in RTP code.
+	  SIP requests that offered codecs incompatible with configured
+	  values could result in the allocation of RTP and RTCP ports that
+	  would not get reclaimed later. ASTERISK-24666 #close Reported by
+	  Y Ateya Review: https://reviewboard.asterisk.org/r/4323
+	  AST-2015-001 ........ Merged revisions 431300 from
+	  http://svn.asterisk.org/svn/asterisk/branches/12
+
+	* funcs/func_curl.c, /: Multiple revisions 431297-431298 ........
+	  r431297 | mmichelson | 2015-01-28 11:05:26 -0600 (Wed, 28 Jan
+	  2015) | 17 lines Mitigate possible HTTP injection attacks using
+	  CURL() function in Asterisk. CVE-2014-8150 disclosed a
+	  vulnerability in libcURL where HTTP request injection can be
+	  performed given properly-crafted URLs. Since Asterisk makes use
+	  of libcURL, and it is possible that users of Asterisk may get
+	  cURL URLs from user input or remote sources, we have made a patch
+	  to Asterisk to prevent such HTTP injection attacks from
+	  originating from Asterisk. ASTERISK-24676 #close Reported by Matt
+	  Jordan Review: https://reviewboard.asterisk.org/r/4364
+	  AST-2015-002 ........ r431298 | mmichelson | 2015-01-28 11:12:49
+	  -0600 (Wed, 28 Jan 2015) | 3 lines Fix compilation error from
+	  previous patch. ........ Merged revisions 431297-431298 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11 ........ Merged
+	  revisions 431299 from
+	  http://svn.asterisk.org/svn/asterisk/branches/12
+
+2015-01-28 12:18 +0000 [r431267]  Sean Bright <sean at malleable.com>
+
+	* res/res_format_attr_silk.c, res/res_format_attr_opus.c: media
+	  formats: update res_format_attr_opus & silk In r419044, we
+	  changed how formats were handled, but the return value of the
+	  format_parse_sdp_fmtp functions in res_format_attr_opus and
+	  res_format_attr_silk were not updated, causing calls to fail. Ran
+	  into this when getting codec_opus working with Asterisk 13. Once
+	  the return value was corrected, we were crashing in opus_getjoint
+	  because of NULL format attributes. I've fixed this as well in
+	  this patch. Review: https://reviewboard.asterisk.org/r/4371/
+
+2015-01-28 04:09 +0000 [r431243]  Richard Mudgett <rmudgett at digium.com>
+
+	* main/sorcery.c, res/res_pjsip_outbound_registration.c,
+	  res/res_pjsip.c: res_pjsip_outbound_registration: Fix reload race
+	  condition. Performing a CLI "module reload" command when there
+	  are new pjsip.conf registration objects defined frequently failed
+	  to load them correctly. What happens is a race condition between
+	  res_pjsip pushing its reload into an asynchronous task processor
+	  task and the thread that does the rest of the reloads when it
+	  gets to reloading the res_pjsip_outbound_registration module. A
+	  similar race condition happens between a reload and the CLI/AMI
+	  show registrations commands. The reload updates the
+	  current_states container and the CLI/AMI commands call
+	  get_registrations() which builds a new current_states container.
+	  * Made res_pjsip.c reload_module() use
+	  ast_sip_push_task_synchronous() instead of ast_sip_push_task() to
+	  eliminate two threads processing config reloads at the same time.
+	  * Made get_registrations() not replace the global current_states
+	  container so the CLI/AMI show registrations command cannot
+	  interfere with reloading. You could never add/remove objects in
+	  the container without the possibility of the container being
+	  replaced out from under you by get_registrations(). * Added a
+	  registration loaded sorcery instance observer to purge any dead
+	  registration objects since get_registrations() cannot do this job
+	  anymore. The struct ast_sorcery_instance_observer callbacks must
+	  be used because the callback happens inline with the load
+	  process. The struct ast_sorcery_observer callbacks are pushed to
+	  a different thread. * Added some global current_states NULL
+	  pointer checks in case the container disappears because of
+	  unload_module(). * Made sorcery's struct
+	  ast_sorcery_instance_observer.object_type_loaded callbacks
+	  guaranteed to be called before any struct
+	  ast_sorcery_observer.loaded callbacks will be called. * Moved the
+	  check for non-reloadable objects to before the sorcery instance
+	  loading callbacks happen to short circuit unnecessary work.
+	  Previously with non-reloadable objects, the sorcery instance
+	  loading/loaded callbacks would always happen, the individual
+	  wizard loading/loaded would be prevented, and the non-reloadable
+	  type logging message would be logged for each associated wizard.
+	  ASTERISK-24729 #close Review:
+	  https://reviewboard.asterisk.org/r/4381/
+
+2015-01-27 22:56 +0000 [r431179-431219]  Kevin Harwell <kharwell at digium.com>
+
+	* /, main/tcptls.c: tcptls: Bad file descriptor error when
+	  reloading chan_sip While running through some scenarios using
+	  chan_sip and tcp a problem would occur that resulted in a flood
+	  of bad file descriptor messages on the cli: tcptls.c:712
+	  ast_tcptls_server_root: Accept failed: Bad file descriptor The
+	  message is received because the underlying socket has been
+	  closed, so is valid. This is probably happening because unloading
+	  of chan_sip is not atomic. That however is outside the scope of
+	  this patch. This patch simply stops the logging of multiple
+	  occurrences of that message. ASTERISK-24728 #close Reported by:
+	  Thomas Thompson Review: https://reviewboard.asterisk.org/r/4380/
+	  ........ Merged revisions 431218 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+	* /, channels/chan_sip.c: chan_sip: stale nonce causes failure When
+	  refreshing (with a small expiration) a registration that was sent
+	  to chan_sip the nonce would be considered stale and reject the
+	  registration. What was happening was that the initial
+	  registration's "dialog" still existed in the dialogs container
+	  and upon refresh the dialog match algorithm would choose that as
+	  the "dialog" instead of the newly created one. This occurred
+	  because the algorithm did not check to see if the from tag
+	  matched if authentication info was available after the 401. So,
+	  it ended up assuming the original "dialog" was a match and
+	  stopped the search. The old "dialog" of course had an old nonce,
+	  thus the stale nonce message. This fix attempts to leave the
+	  original functionality alone except in the case of a REGISTER. If
+	  a REGISTER is received if searches for an existing "dialog"
+	  matching only on the callid. If the expires value is low enough
+	  it will reuse dialog that is there, otherwise it will create a
+	  new one. ASTERISK-24715 #close Reported by: John Bigelow Review:
+	  https://reviewboard.asterisk.org/r/4367/ ........ Merged
+	  revisions 431187 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+	* res/res_pjsip/pjsip_outbound_auth.c, res/res_pjsip/config_auth.c,
+	  main/stasis_message_router.c, res/res_pjsip/location.c,
+	  res/res_pjsip/pjsip_configuration.c,
+	  res/res_pjsip/pjsip_distributor.c,
+	  res/res_pjsip/include/res_pjsip_private.h,
+	  res/res_pjsip/pjsip_global_headers.c,
+	  res/res_pjsip/pjsip_options.c, res/res_pjsip.c,
+	  res/res_pjsip/config_transport.c: res_pjsip: make it unloadable
+	  (take 2) Due to the original patch causing memory corruptions it
+	  was removed until the problem could be resolved. This patch is
+	  the original patch plus some added locking around stasis router
+	  subcription that was needed to avoid the memory corruption.
+	  Description of the original problem and patch (still applicable):
+	  The res_pjsip module was previously unloadable. With this patch
+	  it can now be unloaded. This patch is based off the original
+	  patch on the issue (listed below) by Corey Farrell with a few
+	  modifications. Namely, removed a few changes not required to make
+	  the module unloadable and also fixed a bug that would cause
+	  asterisk to crash on unloading. This patch is the first step
+	  (should hopefully be followed by another/others at some point) in
+	  allowing res_pjsip and the modules that depend on it to be
+	  unloadable. At this time, res_pjsip and some of the modules that
+	  depend on res_pjsip cannot be unloaded without causing problems
+	  of some sort. The goal of this patch is to get res_pjsip and only
+	  res_pjsip to be able to unload successfully and/or shutdown
+	  without incident (crashes, leaks, etc...). Other dependent
+	  modules may still cause problems on unload. Basically made sure,
+	  with the patch applied, that res_pjsip (with no other dependent
+	  modules loaded) could be succesfully unloaded and Asterisk could
+	  shutdown without any leaks or crashes that pertained directly to
+	  res_pjsip. ASTERISK-24485 #close Reported by: Corey Farrell
+	  Review: https://reviewboard.asterisk.org/r/4363/ patches:
+	  pjsip_unload-broken-r1.patch submitted by Corey Farrell (license
+	  5909)
+
+2015-01-27 17:36 +0000 [r431160]  Richard Mudgett <rmudgett at digium.com>
+
+	* /, apps/confbridge/include/confbridge.h, apps/app_confbridge.c:
+	  app_confbridge: Repeatedly starting and stopping recording ref
+	  leaks the recording channel. Starting and stopping conference
+	  recording more than once causes the recording channels to be
+	  leaked. For v13 the channels also show up in the CLI "core show
+	  channels" output. * Reworked and simplified the recording channel
+	  code to use ast_bridge_impart() instead of managing the recording
+	  thread in the ConfBridge code. The recording channel's ref
+	  handling easily falls into place and other off nominal code paths
+	  get handled better as a result. ASTERISK-24719 #close Reported
+	  by: John Bigelow Review: https://reviewboard.asterisk.org/r/4368/
+	  Review: https://reviewboard.asterisk.org/r/4369/ ........ Merged
+	  revisions 431135 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2015-01-27 17:32 +0000 [r431157]  Joshua Colp <jcolp at digium.com>
+
+	* main/bridge_channel.c, res/res_pjsip_sdp_rtp.c: bridge /
+	  res_pjsip_sdp_rtp: Fix issues with media not being reinvited
+	  during direct media. This change fixes two issues: 1. During a
+	  swap operation bridging added the new channel before having the
+	  swap channel leave. This was not handled in bridge_native_rtp and
+	  could result in a channel not getting reinvited back to Asterisk.
+	  After this change the swap channel will leave first and the new
+	  channel will then join. 2. If a re-invite was received after a
+	  session had been established any upstream elements (such as
+	  bridge_native_rtp) were not notified that they may want to
+	  re-evaluate things. After this change an UPDATE_RTP_PEER control
+	  frame is queued when this situation occurs and upstream can
+	  react. AST-1524 #close Review:
+	  https://reviewboard.asterisk.org/r/4378/
+
+2015-01-27 17:22 +0000 [r431153]  Jonathan Rose <jrose at digium.com>
+
+	* main/manager.c: Manager: Fix Manager Action ModuleLoad to give
+	  correct response when reloading Prior to this patch, ModuleLoad
+	  would respond with an error indicating that the requested module
+	  wasn't found in spite of finding and reloading the module.
+	  Review: https://reviewboard.asterisk.org/r/4373/ ASTERISK-24721
+	  #close
+
+2015-01-27 17:20 +0000 [r431134-431145]  Matthew Jordan <mjordan at digium.com>
+
+	* res/ari/resource_bridges.c,
+	  rest-api-templates/asterisk_processor.py,
+	  res/ari/resource_channels.h, res/res_ari_bridges.c,
+	  res/ari/resource_bridges.h, rest-api-templates/api.wiki.mustache,
+	  rest-api/api-docs/channels.json,
+	  rest-api-templates/swagger_model.py,
+	  rest-api/api-docs/bridges.json: ARI: Improve wiki documentation
+	  This patch improves the documentation of ARI on the wiki.
+	  Specifically, it addresses the following: * Allowed values and
+	  allowed ranges weren't documented. This was particularly
+	  frustrating, as Asterisk would reject query parameters with
+	  disallowed values - but we didn't tell anyone what the allowed
+	  values were. * The /play/id operation on /channels and /bridges
+	  failed to document all of the added media resource types. *
+	  Documentation for creating a channel into a Stasis application
+	  failed to note when it occurred, and that creating a channel into
+	  Stasis conflicts with creating a channel into the dialplan. *
+	  Some other minor tweaks in the mustache templates, including
+	  italicizing the parameter type, putting the default value on its
+	  own sub-bullet, and some other nicities. Review:
+	  https://reviewboard.asterisk.org/r/4351
+
+	* apps/confbridge/conf_config_parser.c,
+	  apps/confbridge/include/confbridge.h: app_confbridge: Restore
+	  user's menu name to CLI output of 'confbridge list' When issuing
+	  a 'confbridge list XXXX' CLI command, the resulting output no
+	  longer displays the menu associated with a ConfBridge
+	  participant. The issue was caused by ASTERISK-22760. When that
+	  patch was done, it removed the copying of the menu name
+	  associated with the user from the actual user profile. This patch
+	  fixes the issue by copying the menu name over to the user profile
+	  when the menu hooks are applied to the user. Since that function
+	  now does a little bit more than just apply the hooks, the name of
+	  the function has been changed to cover the copying of the menu
+	  name over as well. In addition, there is a disparity between the
+	  menu name length as it is stored on the conf_menu structure and
+	  the confbridge_user structure; this patch makes the lengths match
+	  so that a strcpy can be used. Review:
+	  https://reviewboard.asterisk.org/r/4372/ ASTERISK-24723 #close
+	  Reported by: Steve Pitts
+
+2015-01-27 11:47 +0000 [r431114]  Joshua Colp <jcolp at digium.com>
+
+	* res/parking/parking_manager.c: res_parking: Fix crash due to race
+	  condition when unloading. There is currently a race condition
+	  when unloading the res_parking module. Depending on the will of
+	  the universe the subscription invocation may occur AFTER the
+	  module is unloaded. This is because the module does NOT use
+	  stasis_unsubscribe_and_join when terminating the subscription. It
+	  merely uses stasis_unsubscribe. This change makes it use
+	  stasis_unsubscribe_and_join which is documented for usage in this
+	  exact scenario. AST-1520 #close Review:
+	  https://reviewboard.asterisk.org/r/4375/
+
+2015-01-26 14:49 +0000 [r431092]  David M. Lee <dlee at digium.com>
+
+	* channels/sip/include/route.h, funcs/func_presencestate.c,
+	  main/rtp_engine.c, configure, include/asterisk/autoconfig.h.in,
+	  include/asterisk/sem.h, configure.ac, main/app.c,
+	  main/bridge_channel.c, main/sem.c, res/res_timing_kqueue.c,
+	  main/asterisk.c: Various fixes for OS X This patch addresses
+	  compilation errors on OS X. It's been a while, so there's quite a
+	  few things. * Fixed __attribute__ decls in route.h to be
+	  portable. * Fixed htonll and ntohll to work when they are defined
+	  as macros. * Replaced sem_t usage with our ast_sem wrapper. *
+	  Added ast_sem_timedwait to our ast_sem wrapper. * Fixed some GCC
+	  4.9 warnings using sig*set() functions. * Fixed some format
+	  strings for portability. * Fixed compilation issues with
+	  res_timing_kqueue (although tests still fail on OS X). * Fixed
+	  menuconfig /sbin/launchd detection, which disables
+	  res_timing_kqueue on OS X). ASTERISK-24539 #close Reported by:
+	  George Joseph ASTERISK-24544 #close Reported by: George Joseph
+	  Review: https://reviewboard.asterisk.org/r/4327/
+
+2015-01-25 13:42 +0000 [r431072]  Matthew Jordan <mjordan at digium.com>
+
+	* main/config.c: dynamic realtime: Updates fail to work due to
+	  update fields being passed over When a crash was fixed due to
+	  usage of the REALTIME function in r423003, a regression was
+	  introduced into ast_update2_realtime where the update fields
+	  passed to the function would be skipped and the lookup field
+	  processed twice. The use of this function is a bit interesting: A
+	  variable argument list is used with two sentinel values - the
+	  first marks the end of the lookup fields/values; the second marks
+	  the end of the update fields/values. Unfortunately,
+	  ast_update2_realtime parses over the lookup fields twice, as
+	  opposed to parsing over the update fields. This causes the
+	  lookups to succeed, but the updates itself to have no effect.
+	  Note that the most common instance of this problem occurred in
+	  app_voicemail during the updating of a mailbox password. Thanks
+	  to the issue reporter, Paddy Grice, for pointing out the problem.
+	  Review: https://reviewboard.asterisk.org/r/4356/ ASTERISK-24231
+	  ASTERISK-24626 #close Reported by: Paddy Grice
+
+2015-01-23 20:13 +0000 [r431050-431052]  Richard Mudgett <rmudgett at digium.com>
+
+	* apps/confbridge/conf_chan_record.c: app_confbridge: Make CBRec
+	  channel names more unique. Channel names should be different from
+	  other channels in the system while the channel exists. * Use a
+	  sequence number for CBRec channels instead of a random number
+	  because the same random number could be picked again for the next
+	  CBRec channel.
+
+	* /, apps/app_confbridge.c: app_confbridge: Whitespace Because
+	  there is sometimes no sence to any whitespace. ........ Merged
+	  revisions 431049 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2015-01-23 17:08 +0000 [r431030]  David M. Lee <dlee at digium.com>
+
+	* res/res_pjsip_config_wizard.c: Add depend on pjproject to
+	  res_pjsip_config_wizard.c
+
+2015-01-23 15:12 +0000 [r430999]  Kevin Harwell <kharwell at digium.com>
+
+	* res/parking/parking_applications.c, channels/chan_iax2.c,
+	  res/res_pjsip/pjsip_global_headers.c, res/res_pjsip_pubsub.c,
+	  res/res_ari_channels.c, res/res_stasis.c,
+	  rest-api-templates/param_parsing.mustache,
+	  res/res_ari_endpoints.c, res/res_ari_events.c,
+	  include/asterisk/stasis_app.h, res/res_pjsip_mwi.c: Investigate
+	  and fix memory leaks in Asterisk Fixed memory leaks that were
+	  found in Asterisk. ASTERISK-24693 #close Reported by: Kevin
+	  Harwell Review: https://reviewboard.asterisk.org/r/4347/
+
+2015-01-23 15:03 +0000 [r430994-430998]  Walter Doekes <walter+asterisk at wjd.nu>
+
+	* apps/app_voicemail.c, channels/chan_unistim.c,
+	  funcs/func_hangupcause.c, main/manager_bridges.c,
+	  channels/chan_misdn.c, funcs/func_groupcount.c, /,
+	  addons/ooh323c/src/ooh245.c, channels/chan_sip.c, res/res_fax.c,
+	  res/res_pjsip_outbound_registration.c, apps/app_minivm.c,
+	  apps/app_alarmreceiver.c, include/asterisk/channel.h,
+	  contrib/utils/eagi_proxy.c: Fix typo's (retrieve, specified,
+	  address). ........ Merged revisions 430996 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+	* /, channels/chan_sip.c: chan_sip: Case insensitive comparison of
+	  "defaultuser" parameter. All the other configuration options are
+	  case insensitive, so this one should be too. ASTERISK-24355
+	  #close Reported by: HZMI8gkCvPpom0tM patches: ast.patch uploaded
+	  by HZMI8gkCvPpom0tM (License 6658) ........ Merged revisions
+	  430993 from http://svn.asterisk.org/svn/asterisk/branches/11
+
+2015-01-22 19:24 +0000 [r430957-430975]  Richard Mudgett <rmudgett at digium.com>
+
+	* include/asterisk/bridge.h,
+	  include/asterisk/bridge_channel_internal.h, main/bridge.c,
+	  include/asterisk/bridge_internal.h, main/bridge_channel.c: Bridge
+	  core: Pass a ref with the swap channel when joining a bridge.
+	  When code imparts a channel into a bridge to swap with another
+	  channel, a ref needs to be held on the swap channel to ensure
+	  that it cannot dissapear before finding it in the bridge. * The
+	  ast_bridge_join() swap channel parameter now always steals a ref
+	  for the swap channel. This is the only change to the bridge
+	  framework's public API semantics. *
+	  bridge_channel_internal_join() now requires the
+	  bridge_channel->swap channel to pass in a ref. ASTERISK-24649
+	  Reported by: John Bigelow Review:
+	  https://reviewboard.asterisk.org/r/4354/
+
+	* res/res_pjsip_outbound_registration.c:
+	  res_pjsip_outbound_registration.c: Minor code cleanup. * Add an
+	  allocation failure check and assert in
+	  sip_outbound_registration_response_cb(). * Made
+	  sip_outbound_registration_state_destroy() handle partially
+	  created state objects from
+	  sip_outbound_registration_state_alloc(). Review:
+	  https://reviewboard.asterisk.org/r/4366/
+
+2015-01-22 18:09 +0000 [r430939]  Scott Griepentrog <sgriepentrog at digium.com>
+
+	* res/stasis/app.c, res/stasis/stasis_bridge.c: stasis transfer:
+	  fix a race condition on stasis bridge push After a bridge
+	  transfer completes where a local replacement channel is used, a
+	  stasis transfer message with the details of the transfer is sent.
+	  This is processed by stasis which then sets the stasis app name
+	  and replaced channel snapshot on the replacement channel.
+	  However, since a separate thread was already started to run
+	  stasis on the new replacement channel, a race was on to see if
+	  the message processing would be completed before the app name was
+	  needed, otherwise the channel would be hung up. This change moves
+	  the calls used to set the stasis app name and the replace
+	  snapshot to the bridge_stasis_push function callback from the
+	  bridge transfer logic, allowing the steps to be completed earlier
+	  and more deterministically, and the race elimianted. NOTE: the
+	  swap channel parameter to bridge_stasis_push (and thus all bridge
+	  push callbacks) must always be present when performing a swap
+	  with another channel. ASTERISK-24649 #close Reported by: John
+	  Bigelow Review: https://reviewboard.asterisk.org/r/4341/
+
+2015-01-22 14:23 +0000 [r430921]  Matthew Jordan <mjordan at digium.com>
+
+	* /, apps/app_voicemail.c: apps/app_voicemail: Trigger MWI
+	  notification with MixMonitor m() option The MixMonitor m() option
+	  allows a recording to be pushed to a specific voicemail mailbox.
+	  If the message is delivered to the mailbox's INBOX, however, no
+	  MWI notification is currently raised. This patch corrects the
+	  issue by properly calling notify_new_state from the
+	  msg_create_from_file function. This will cause MWI to be
+	  triggered if the message was placed in the mailbox's INBOX.
+	  ASTERISK-24709 #close Reported by: Gareth Palmer patches:
+	  app_voicemail-430919.patch uploaded by Gareth Palmer (License
+	  5169) ........ Merged revisions 430920 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2015-01-21 21:53 +0000 [r430902]  Richard Mudgett <rmudgett at digium.com>
+
+	* res/res_pjsip_outbound_registration.c:
+	  res_pjsip_outbound_registration.c: Move unref to a better place.
+	  Move an unconditional unref of client_state so it doesn't look
+	  like it could be used after the last ref has destroyed it.
+
+2015-01-21 13:33 +0000 [r430840-430864]  Matthew Jordan <mjordan at digium.com>
+
+	* channels/chan_sip.c: channels/chan_sip: Fix registration leak
+	  during reload When the SIP registrations were migrated to using
+	  ao2 in what was then trunk, the explicit destruction of the
+	  registrations on module reload was removed and not replaced with
+	  an ao2 equivalent. Debugging done by Stefan Engström, the issue
+	  reporter, on ASTERISK-24673 confirmed that the reference in the
+	  registry_list container was being leaked. Since the purpose of
+	  cleanup_all_regs is to prep a registration for destruction, this
+	  function now calls an ao2_callback function callback with the
+	  OBJ_MULTIPLE | OBJ_NODATA | OBJ_UNLINK flags used to remove the
+	  registrations. This cleans up each registration, and also removes
+	  it from the registration container registry_list. Review:
+	  https://reviewboard.asterisk.org/r/4355/ ASTERISK-24640 #close
+	  Reported by: Max Man ASTERISK-24673 #close Reported by: Stefan
+	  Engström Tested by: Stefan Engström
+
+	* cdr/cdr_manager.c, cel/cel_manager.c: AMI: Add documentation for
+	  the missing Cdr/CEL events. This patch adds AMI event
+	  documentation for the Cdr and CEL AMI events. Note that while
+	  these events do share fields with each other and with other
+	  channel related events, they do not contain all of the fields in
+	  a standard channel snapshot, nor is the description of the fields
+	  identical. As such, the patch opts for documentation for each
+	  field, for each event. Review:
+	  https://reviewboard.asterisk.org/r/4350/ ASTERISK-24671 #close
+	  Reported by: Dan Jenkins
+
+	* apps/app_dial.c: apps/app_dial: Don't publish DialEnd twice on
+	  unexpected GoSub/Macro values The Dial application has some
+	  interesting options with the mid-call Macro (M) and GoSub (U)
+	  options. If the MACRO_RESULT/GOSUB_RESULT returns specific
+	  values, the Dial application will take some action upon the
+	  channels involved in the dial operation (such as hanging up a
+	  particular party, etc.) The Dial application ensures that a
+	  Stasis message is published in the event that
+	  MACRO_RESULT/GOSUB_RESULT returns a value that kills the dial
+	  operation, so that there is a corresponding DialEnd event
+	  published in AMI/ARI for the DialBegin event that preceeded it. A
+	  bug exists where that same DialEnd event will be published on
+	  Stasis even if the value returned in MACRO_RESULT/GOSUB_RESULT is
+	  not one that the Dial application cares about. This causes two
+	  DialEnd events to be published - one with the
+	  MACRO_RESULT/GOSUB_RESULT and another with "ANSWERED" - which is
+	  all sorts of wrong. This patch fixes the bug by ensuring that we
+	  only publish a DialEnd message to Stasis if the Dial
+	  application's mid-call Macro/GoSub returns something that Dial
+	  cares about. Review: https://reviewboard.asterisk.org/r/4336
+	  ASTERISK-24682 #close Reported by: Matt Jordan
+
+	* main/rtp_engine.c: main/rtp_engine: Format NTP timestamps as
+	  unsigned longs When the RTCP reports are created, the NTP
+	  timestamps are stored as strings, as JSON does not have an
+	  integer type long enough to store the value. However, on 32-bit
+	  systems, a signed long may overflow for some portion of the
+	  timestamp. This patch corrects the overflow by formatting the
+	  timestamps as unsigned longs.
+
+2015-01-20 16:51 +0000 [r430818]  asanders <asanders at localhost>:
+
+	* res/ari/resource_bridges.c: ARI: Fixed crash that occurred when
+	  updating a bridge when the optional query parameter 'name' was
+	  not supplied. Prior to this changeset, posting to the:
+	  /ari/bridges/{bridgeId} endpoint without specifying a value for
+	  the [name] query parameter, would crash Asterisk if the bridge
+	  you are attempting to create (or update) had the same ID as an
+	  existing bridge. The internal mechanism of the POST operation
+	  interpreted a null value for name, thus resulting in an error
+	  condition that crashed Asterisk. ASTERISK-24560 #close Reported
+	  By: Kinsey Moore Review: https://reviewboard.asterisk.org/r/4349/
+
+2015-01-20 16:46 +0000 [r430817]  Richard Mudgett <rmudgett at digium.com>
+
+	* configs/samples/iax.conf.sample, res/res_fax.c,
+	  funcs/func_channel.c, UPGRADE.txt, res/snmp/agent.c,
+	  channels/chan_iax2.c: CHANNEL(peer), chan_iax2, res_fax, SNMP
+	  agent: Fix deadlock from reaching across a bridge. Calling
+	  ast_channel_bridge_peer() cannot be done while holding any
+	  channel locks. The reported issue hit the deadlock in chan_iax2,
+	  but an audit of the ast_channel_bridge_peer() calls found three
+	  more locations where the same deadlock can occur. * Made
+	  CHANNEL(peer), res_fax, and the SNMP agent not call
+	  ast_channel_bridge_peer() with any channel locked. For
+	  CHANNEL(peer) I had to rework the logic to not hold the channel
+	  lock. * Made chan_iax2 no longer call ast_channel_bridge_peer().
+	  It was done for legacy reasons that no longer apply. * Removed
+	  the iax.conf forcejitterbuffer option. It is now always enabled
+	  when the jitterbuffer option is enabled. If you put a jitter
+	  buffer on a channel it will be on the channel. ASTERISK-24600
+	  #close Reported by: Jeff Collell Review:
+	  https://reviewboard.asterisk.org/r/4342/
+
+2015-01-20 02:39 +0000 [r430796-430799]  Matthew Jordan <mjordan at digium.com>
+
+	* contrib/scripts/install_prereq, /:
+	  contrib/scripts/install_prereq: Don't install 32-bit packages on
+	  64-bit hosts On Debian based systems, the install_prereq tool
+	  uses a search command on Debian that results in selecting both
+	  64-bit and 32-bit packages. Besides the waste of disk space, this
+	  can actually cause aptitude use 100% of memory on a VM with 1GB
+	  of RAM as it tried to work out all of the 32-bit package
+	  dependencies. This patch filters out the 32-bit packages on a
+	  64-bit machine, and leaves 32-bit machines alone. ASTERISK-24048
+	  #close Reported by: Ben Klang Tested by: Ben Klang, Matt Jordan
+	  patches: install_prereq_64-bit_compat.patch uploaded by Ben Klang
+	  (License 5876) ........ Merged revisions 430798 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+	* apps/app_voicemail.c, /: app_voicemail: Temp message left after
+	  review/hangup with ODBC/IMAP backend When using ODBC or IMAP
+	  storage, temporary files created on the file system must be
+	  disposed of using the DISPOSE macro. The DELETE macro will map to
+	  a deletion function for the backend storage, but does not clean
+	  up any local files created as a result of the operation. When
+	  using voicemail with the operator and review options enabled,
+	  pressing 0 to enter the menu, followed by 1 to save the message,
+	  followed by any other DTMF press to delete the message, will
+	  result in the temporary file lingering on the file system. This
+	  patch properly calls DISPOSE after the DELETE. This causes the
+	  local file to be disposed of. ASTERISK-24288 #close Reported by:
+	  LEI FU patches: voicemail_odbc_review_fix.diff uploaded by LEI FU
+	  (License 6640) ........ Merged revisions 430795 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2015-01-19 18:05 +0000 [r430776]  Mark Michelson <mmichelson at digium.com>
+
+	* main/pbx.c: Call extension state callbacks at hint creation. When
+	  a hint gets created, any subsequent device or presence state
+	  changes result in extension status events getting sent out to
+	  interested parties. However, at the time of hint creation, no
+	  such event gets sent out, so watchers of extension state are
+	  potentially left in the dark until the first state change after
+	  hint creation. Patch contributed by John Hardin (License #6512)
+
+2015-01-19 13:18 +0000 [r430755]  Joshua Colp <jcolp at digium.com>
+
+	* res/res_pjsip_multihomed.c, res/res_pjsip.c: res_pjsip /
+	  res_pjsip_multihomed: Use the correct transport and addressing
+	  information on UAS sessions. The first thing this patch fixes is
+	  UAS dialogs. Previously if a transport was configured on an
+	  endpoint and an inbound session was created there was no
+	  guarantee that requests sent on the dialog would use the correct
+	  transport and address information. This has now been fixed so an
+	  explicitly configured transport is taken into account. The second
+	  thing this patch fixes is res_pjsip_multihomed. The
+	  res_pjsip_multihomed module attempts to determine what transport
+	  a message should go out on and what addressing information should
+	  go into the message itself. In a scenario where multiple
+	  transports exist bound to the same IP address but a different
+	  port the code would incorrectly alter the transport and change
+	  the message to the wrong transport. This change makes the
+	  res_pjsip_multihomed module smarter so it will only change the
+	  transport and address information in the message when it is
+	  possible and makes sense. ASTERISK-24615 #close Reported by:
+	  David Justl Review: https://reviewboard.asterisk.org/r/4331/
+
+2015-01-17 00:31 +0000 [r430734]  Kevin Harwell <kharwell at digium.com>
+
+	* res/res_pjsip/config_transport.c,
+	  res/res_pjsip/pjsip_outbound_auth.c, res/res_pjsip/config_auth.c,
+	  main/stasis_message_router.c, res/res_pjsip/location.c,
+	  res/res_pjsip/pjsip_configuration.c,
+	  res/res_pjsip/pjsip_distributor.c,
+	  res/res_pjsip/include/res_pjsip_private.h,
+	  res/res_pjsip/pjsip_global_headers.c,
+	  res/res_pjsip/pjsip_options.c, res/res_pjsip.c: REVERTING
+	  res_pjsip: make it unloadable Due to the original patch causing
+	  memory corruptions the patch is being removed until the problem
+	  can be resolved.
+
+2015-01-16 22:13 +0000 [r430709-430716]  Mark Michelson <mmichelson at digium.com>
+
+	* CHANGES: Change PJProject version requirement for ca_list_path
+	  transport option in CHANGES file.
+
+	* channels/chan_pjsip.c, res/res_pjsip_session.c: Fix problem where
+	  a hung channel could occur on a failed blind transfer. Different
+	  clients react differently to being told that a blind transfer has
+	  failed. Some will simply send a BYE and be done with it. Others
+	  will attempt to reinvite themselves back onto the call. In the
+	  latter case, we were creating a new channel and then leaving it
+	  to sit forever doing nothing. With this code change, that new
+	  channel will not be created and the dialog with the transferring
+	  channel will be cleaned up properly. ASTERISK-24624 #close
+	  Reported by Zane Conkle Review:
+	  https://reviewboard.asterisk.org/r/4339
+
+	* include/asterisk/res_pjsip.h, configure,
+	  include/asterisk/autoconfig.h.in, configure.ac,
+	  configs/samples/pjsip.conf.sample, CHANGES, res/res_pjsip.c,
+	  res/res_pjsip/config_transport.c: Add support for the
+	  ca_list_path option for PJSIP transports. This allows for a path
+	  to be specified that has a collection of CA certificates in it.
+	  ASTERISK-24575 #close Reported by cloos Patches:
+	  pj-ca-path-trunk.diff uploaded by cloos (License #5956) Review:
+	  https://reviewboard.asterisk.org/r/4344
+
+2015-01-15 17:35 +0000 [r430685-430687]  Richard Mudgett <rmudgett at digium.com>
+
+	* res/res_fax_spandsp.c, res/res_fax.c: res_fax.c,
+	  res_fax_spandsp.c: Remove redundant locking. When FAX was
+	  developed, apparently the faxregistry.container used to be a
+	  linked list that was converted to an ao2 container. Some of the
+	  replacement ao2 container operations still had explicit
+	  lock/unlocks around them. Three off nominal code paths in
+	  res_fax.c and res_fax_spandsp.c unlock the channel even though
+	  the routine did not lock the channel and other code paths in the
+	  routine do not unlock the channel. Review:
+	  https://reviewboard.asterisk.org/r/4340/
+
+	* res/res_fax_spandsp.c, res/res_fax.c: res_fax.c,
+	  res_fax_spandsp.c: Fix some curlies on the end of function
+	  definitions.
+
+2015-01-15 12:09 +0000 [r430664]  Joshua Colp <jcolp at digium.com>
+
+	* res/res_pjsip_outbound_registration.c:
+	  res_pjsip_outbound_registration: Fix race condition when
+	  reloading and listing registrations. Due to the split of outbound
+	  registration state from configuration it is possible during a
+	  reload for a "pjsip show registrations" CLI command to be
+	  executed which gets an older snapshot of the configuration. This
+	  configuration may include outbound registrations which have been
+	  removed due to a reload operation occurring at the same time. The
+	  code for printing the outbound registration did not take this
+	  into account but now it does. AST-1506 #close Review:
+	  https://reviewboard.asterisk.org/r/4338/
+
+2015-01-15 02:18 +0000 [r430646]  Matthew Jordan <mjordan at digium.com>
+
+	* configure, configure.ac: configure: If cross-compiling, assume we
+	  have working semaphores The Asterisk 13 configure.ac checks for
+	  HAS_WORKING_SEMAPHORE but does not have an option for
+	  cross-compiling so it fails with an exit. Since we're cross-
+	  compiling, we can't exactly go looking for the header. The
+	  semaphore.h header is relatively common: * It's part of the POSIX
+	  standard * It's part of GNU C Library As such, we assume that it
+	  will be present when cross-compiling. As such, this patch
+	  defaults "HAS_WORKING_SEMAPHORE" to "1" if cross-compiling is
+	  detected. If you're cross-compiling to a platform that doesn't
+	  support this, then make sure you re-define this to 0.
+	  ASTERISK-24663 #close Reported by: abelbeck patches:
+	  asterisk-13-anonymous-semaphores.patch uploaded by abelbeck
+	  (License 5903)
+
+2015-01-14 23:14 +0000 [r430628]  Kevin Harwell <kharwell at digium.com>
+
+	* res/res_pjsip/pjsip_configuration.c,
+	  res/res_pjsip/pjsip_distributor.c,
+	  res/res_pjsip/include/res_pjsip_private.h,
+	  res/res_pjsip/pjsip_global_headers.c,
+	  res/res_pjsip/pjsip_options.c, res/res_pjsip.c,
+	  res/res_pjsip/config_transport.c,
+	  res/res_pjsip/pjsip_outbound_auth.c, res/res_pjsip/config_auth.c,
+	  main/stasis_message_router.c, res/res_pjsip/location.c:
+	  res_pjsip: make it unloadable The res_pjsip module was previously
+	  unloadable. With this patch it can now be unloaded. This patch is
+	  based off the original patch on the issue (listed below) by Corey
+	  Farrell with a few modifications. Namely, removed a few changes
+	  not required to make the module unloadable and also fixed a bug
+	  that would cause asterisk to crash on unloading. This patch is
+	  the first step (should hopefully be followed by another/others at
+	  some point) in allowing res_pjsip and the modules that depend on
+	  it to be unloadable. At this time, res_pjsip and some of the
+	  modules that depend on res_pjsip cannot be unloaded without
+	  causing problems of some sort. The goal of this patch is to get
+	  res_pjsip and only res_pjsip to be able to unload successfully
+	  and/or shutdown without incident (crashes, leaks, etc...). Other
+	  dependent modules may still cause problems on unload. Basically
+	  made sure, with the patch applied, that res_pjsip (with no other
+	  dependent modules loaded) could be succesfully unloaded and
+	  Asterisk could shutdown without any leaks or crashes that
+	  pertained directly to res_pjsip. ASTERISK-24485 #close Reported
+	  by: Corey Farrell Review:
+	  https://reviewboard.asterisk.org/r/4311/ patches:
+	  pjsip_unload-broken-r1.patch submitted by Corey Farrell (license
+	  5909)
+
+2015-01-14 20:27 +0000 [r430608]  Mark Michelson <mmichelson at digium.com>
+
+	* res/res_pjsip_outbound_publish.c: Prevent slow graceful shutdown
+	  when outbound publications never started. The code was missing
+	  the case for explicitly destroying an outbound publication when
+	  Asterisk had never actually published anything. The result was
+	  that Asterisk would hang for a while on a graceful shutdown. With
+	  this change, the case is taken into account, and on a graceful
+	  shutdown, these publications are destroyed without the need to
+	  actually send a PUBLISH request. ASTERISK-24655 #close Reported
+	  by Kevin Harwell Review: https://reviewboard.asterisk.org/r/4325
+
+2015-01-14 15:39 +0000 [r430590]  Matthew Jordan <mjordan at digium.com>
+
+	* build_tools/mkpkgconfig, /: build_tools/mkpkgconfig: Fix Cflags
+	  concatenation error in asterisk.pc The mkpkgconfig script
+	  incorrectly concatenates Cflags options together. As an example,
+	  the following: Cflags: -I/usr/include/libxml2 -g3 Is instead
+	  generated as: Cflags: -I/usr/include/libxml2-g3 This patch
+	  corrects the generation of Cflags in mkpkgconfig such that the
+	  Cflags options are output correctly. Review:
+	  https://reviewboard.asterisk.org/r/3707/ ASTERISK-23991 #close
+	  Reported by: Diederik de Groot patches: fix_mkpkgconfig.diff
+	  uploaded by Diederik de Groot (License 6600) ........ Merged
+	  revisions 430589 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2015-01-13 18:16 +0000 [r430565]  Richard Mudgett <rmudgett at digium.com>
+
+	* apps/app_macro.c, /: app_macro: Don't restore the calling
+	  location on a channel redirect. v11: If a channel redirect to a
+	  macro exten of a macro that is active happens, the redirect
+	  location doesn't get executed. Instead the original macro
+	  location is restored and gets reexecuted. v13: An additional
+	  effect happens if a parked call times out to an extension in the
+	  macro that parked the call then the macro is reexecuted instead
+	  of the expected park return location. * Made not restore the
+	  macro calling location on an AST_SOFTHANGUP_ASYNCGOTO. *
+	  Increased the locked channel range when setting up the macro
+	  execution environment to cover things that should be done while
+	  the channel is locked. * Removed unnecessary NULL tests before
+	  calling ast_free() in _macro_exec(). ASTERISK-23850 #close
+	  Reported by: Andrew Nagy Review:
+	  https://reviewboard.asterisk.org/r/4292/ ........ Merged
+	  revisions 430564 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2015-01-13 12:06 +0000 [r430546]  Joshua Colp <jcolp at digium.com>
+
+	* channels/pjsip/dialplan_functions.c, configure,
+	  include/asterisk/autoconfig.h.in, configure.ac: chan_pjsip: Add
+	  configure check for 'pjsip_get_dest_info' function. The
+	  'pjsip_get_dest_info' function is used to determine if the
+	  signaling transport of the dialog is secure or not. This function
+	  was added in PJSIP 2.3 and does not exist in earlier versions.
+	  This configure check allows Asterisk to build and run with older
+	  versions at the loss of the 'secure' argument for the PJSIP
+	  CHANNEL dialplan function. Usage of this argument will require
+	  upgrading to PJSIP 2.3. ASTERISK-24665 #close Reported by: Mark
+	  Michelson Review: https://reviewboard.asterisk.org/r/4329/
+
+2015-01-12 18:34 +0000 [r430528]  Richard Mudgett <rmudgett at digium.com>
+
+	* include/asterisk/manager.h, main/manager.c: AMI: Revert
+	  non-backwards compatible changes from earlier commit. * Reverted
+	  the change to astman_send_listack() to not use the listflag
+	  parameter and always set the value to "Start" so the start
+	  capitalization is consistent. Unfortunately changing the case of
+	  a returned value is not a backward compatible change so for now
+	  FAXSessions is going to have to remain inconsistent with all of
+	  the other AMI list actions. * Reverted the minor protocol error
+	  fix in action_getconfig() when no requested categories are found.
+	  Each line needs to be formatted as "Header: text". Caught by the
+	  testsuite. ASTERISK-24049
+
+2015-01-12 18:28 +0000 [r430488-430526]  Matthew Jordan <mjordan at digium.com>
+
+	* configs/samples/features.conf.sample:
+	  configs/samples/features.conf.sample: Document attended transfer
+	  DTMF options The sample config was missing the configuration
+	  options for DTMF attended transfer completion scenarios. The
+	  configuration options 'atxferabort', 'atxfercomplete',
+	  'atxferthreeway', and 'atxferswap' are now documented in the
+	  appropriate configuration file. ASTERISK-24678 #close Reported
+	  by: Niklas Larsson patches: features.conf.sample.diff uploaded by
+	  Niklas Larsson (License 5068)
+
+	* main/syslog.c, include/asterisk/syslog.h, /: main/syslog: Allow
+	  dynamic logs, such as security events, to log to the syslog The
+	  security event log uses a dynamic log level (SECURITY) that is
+	  registered with the Asterisk logging core. Unfortunately, the
+	  syslog would ignore log statements that had a dynamic log level
+	  associated with them. Because the syslog cannot handle ad hoc
+	  dynamic log levels, this patch treats any dynamic log entries
+	  sent to the syslog as logs with a level of NOTICE. ASTERISK-20744
+	  #close Reported by: Michael Keuter Tested by: Michael L. Young,
+	  Jacek Konieczny patches:
+	  asterisk-20744-syslog-dynamic-logging_trunk.diff uploaded by
+	  Michael L. Young (license 5026) ........ Merged revisions 430506
+	  from http://svn.asterisk.org/svn/asterisk/branches/11
+
+	* funcs/func_curl.c, /: funcs/func_curl: Fix memory leak when
+	  CURLOPT channel datastore is destroyed When the channel datastore
+	  associated with the usage of CURLOPT on a specific channel is
+	  freed, the underlying structure holding the list of options is
+	  not disposed of. This patch properly frees the structure in the
+	  datastore .destroy callback. ASTERISK-24672 #close Reported by:
+	  Kristian Hogh patches: func_curl-memory-leak.diff uploaded by
+	  Kristian Hogh (License 6639) ........ Merged revisions 430487
+	  from http://svn.asterisk.org/svn/asterisk/branches/11
+
+2015-01-09 22:08 +0000 [r430467-430469]  Scott Griepentrog <sgriepentrog at digium.com>
+
+	* contrib/scripts/sip_to_pjsip/sip_to_pjsip.py,
+	  contrib/scripts/sip_to_pjsip/astconfigparser.py: sip_to_pjsip:
+	  improve ability to parse input files General improvements to SIP
+	  to PJSIP conversion utility: 1) track default section of input
+	  file to allow parsing an include file that doesn't specify a
+	  [section] 2) informatively handle case of assignment without
+	  [section] 3) correctly handle getting sections from included
+	  files - [section]'s are inherited by included file 4) provide
+	  null string as default transport bind ip 5) gracefully handle
+	  missing portions of registration string 6) denote steps of
+	  operation during conversion and confirm top level files as a
+	  convenience ASTERISK-24474 #close Review:
+	  https://reviewboard.asterisk.org/r/4280/ Reported by: John
+	  Kiniston
+
+	* main/features.c: app_bridge: return to the next dialplan priority
+	  When app_bridge grabs a channel and puts it into a bridge, the
+	  channel should then continue where it left off in the dialplan
+	  after the bridge has ended. Although it stores the current
+	  dialplan location as an after bridge goto on the channel, it was
+	  executing the same priority again instead of going to the next
+	  priority. By swapping the "specific" version of
+	  bridge_set_after_goto with bridge_set_after_go_on, the next
+	  priority in the dialplan is executed instead. ASTERISK-24637
+	  #close Review: https://reviewboard.asterisk.org/r/4322/ Reported
+	  by: John Bigelow
+
+2015-01-09 17:54 +0000 [r430434]  Richard Mudgett <rmudgett at digium.com>
+
+	* UPGRADE.txt, res/res_mwi_external_ami.c, CHANGES,
+	  include/asterisk/manager.h, channels/chan_iax2.c,
+	  apps/app_queue.c, apps/app_agent_pool.c,
+	  res/res_manager_devicestate.c, main/manager_bridges.c,
+	  channels/chan_dahdi.c, main/manager.c, channels/chan_skinny.c,
+	  res/res_pjsip_outbound_registration.c,
+	  res/res_manager_presencestate.c,
+	  res/res_pjsip/pjsip_configuration.c, apps/app_confbridge.c,
+	  res/res_pjsip_pubsub.c, main/db.c, res/parking/parking_manager.c,
+	  res/res_pjsip_registrar.c, apps/app_voicemail.c, main/pbx.c,
+	  channels/chan_sip.c, apps/app_meetme.c, main/bridge.c,
+	  res/res_fax.c: AMI: Make AMI actions that generate event lists
+	  consistent. * Made the following AMI actions use list API calls
+	  for consistency: Agents BridgeInfo BridgeList
+	  BridgeTechnologyList ConfbridgeLIst ConfbridgeLIstRooms
+	  CoreShowChannels DAHDIShowChannels DBGet DeviceStateList
+	  ExtensionStateList FAXSessions Hangup IAXpeerlist IAXpeers
+	  IAXregistry MeetmeList MeetmeListRooms MWIGet ParkedCalls
+	  Parkinglots PJSIPShowEndpoint PJSIPShowEndpoints
+	  PJSIPShowRegistrationsInbound PJSIPShowRegistrationsOutbound
+	  PJSIPShowResourceLists PJSIPShowSubscriptionsInbound
+	  PJSIPShowSubscriptionsOutbound PresenceStateList PRIShowSpans
+	  QueueStatus QueueSummary ShowDialPlan SIPpeers SIPpeerstatus
+	  SIPshowregistry SKINNYdevices SKINNYlines Status
+	  VoicemailUsersList * Incremented the AMI version to 2.7.0. *
+	  Changed astman_send_listack() to not use the listflag parameter
+	  and always set the value to "Start" so the start capitalization
+	  is consistent. i.e., The FAXSessions used "Start" while the rest
+	  of the system used "start". The corresponding complete event
+	  always used "Complete". * Fixed ami_show_resource_lists()
+	  "PJSIPShowResourceLists" to output the AMI ActionID for all of
+	  its list events. * Fixed off-nominal AMI protocol error in
+	  manager_bridge_info(), manager_parking_status_single_lot(), and
+	  manager_parking_status_all_lots(). Use of astman_send_error()
+	  after responding to the original AMI action request violates the
+	  action response pattern by sending two responses. * Fixed minor
+	  protocol error in action_getconfig() when no requested categories
+	  are found. Each line needs to be formatted as "Header: text". *
+	  Fixed off-nominal memory leak in
+	  manager_build_parked_call_string(). * Eliminated unnecessary use
+	  of RAII_VAR() in ami_subscription_detail(). ASTERISK-24049 #close
+	  Reported by: Jonathan Rose Review:
+	  https://reviewboard.asterisk.org/r/4315/
+
+2015-01-09 14:51 +0000 [r430416]  Kinsey Moore <kmoore at digium.com>
+
+	* /, res/res_fax.c, include/asterisk/res_fax.h,
+	  configs/samples/res_fax.conf.sample, CHANGES: res_fax: Add T.38
+	  negotiation timeout option This change makes the T.38 negotiation
+	  timeout configurable via 't38timeout' in res_fax.conf or
+	  FAXOPT(t38timeout). It was previously hard coded to be 5000
+	  milliseconds. This change also handles T.38 switch failures by
+	  aborting the fax since in the case where this can happen, both
+	  sides have agreed to switch to T.38 and Asterisk is unable to do
+	  so. Review: https://reviewboard.asterisk.org/r/4320/ ........
+	  Merged revisions 430415 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2015-01-08 21:40 +0000 [r430373-430397]  George Joseph <george.joseph at fairview5.com>
+
+	* res/res_pjsip_pubsub.c: res_pjsip_pubsub: Fix persistent
+	  subscriptions not surviving graceful shutdown If you do a 'core
+	  (shutdown|restart) graceful' persistent subscriptions won't
+	  survive. If you do a 'core (shutdown|restart) now' or asterisk
+	  terminates for some reason, they do. Here's why... When asterisk
+	  shuts down gracefully, it sends a 'NOTIFY/terminated' to
+	  subscribers for each subscription. This not only tells the
+	  subscribers that the dialog/state machine is done, it also frees
+	  the last reference to the subscription tree which causes the
+	  persistent subscription to get deleted from astdb. When asterisk
+	  restarts, nothing's left. Just preventing the delete from astdb
+	  doesn't work because we already told the subscriber to terminate
+	  the dialog so we can't restart it even if it was still in astdb.
+	  Everything works OK if asterisk terminates unexpectedly because
+	  we never send the 'terminated' message so on restart, the
+	  subscription is still in astdb and the subscriber is none the
+	  wiser. This patch suppresses the sending of 'NOTIFY/terminated'
+	  on shutdown for persistent connections. Tested-by: George Joseph
+	  Review: https://reviewboard.asterisk.org/r/4318/
+
+	* res/res_pjsip_outbound_registration.c:
+	  res_pjsip_outbound_registration: Fix reference leak. Every time a
+	  registration started, sip_outbound_registration_response_cb bumps
+	  the ref count on client_state then pushes a
+	  handle_registration_response task. handle_registration_response
+	  never unreffed it though. So every time a registration goes out,
+	  the ref count goes up by one. This patch adds the unreffs to
+	  handle_registration_response. Tested-by: George Joseph Review:
+	  https://reviewboard.asterisk.org/r/4303/
+
+	* res/res_pjsip_outbound_registration.c:
+	  res_pjsip_outbound_registration: Fix several reload issues There
+	  are 2 issues with reloading registrations... 1. The
+	  'can_reuse_registration' test wasn't considering the intervals or
+	  expiration in its determination of whether a registration changed
+	  or not so if you changed any of the intervals or the expiration
+	  and reloaded, the object would get reloaded but the actual timers
+	  wouldn't change. can_reuse_registration now does a sorcery diff
+	  on the old and new objects instead of discretely testing certain
+	  fields. Now if you change expiration for instance, and reload,
+	  the timer is updated and re-registration will occur on the new
+	  value. 2. If you mung up your password on an outbound
+	  registration you get a permanent failure. If you fix the password
+	  (on the outbound_auth object) and reload, nothing tells
+	  outbound_registration to try again because the registration
+	  itself didn't change. This patch adds an observer on the "auth"
+	  object type and if any auth changes, existing registration states
+	  are searched and those in a REJECTED_PERMANENT state are retried.
+	  Tested-by: George Joseph Review:
+	  https://reviewboard.asterisk.org/r/4304/
+
+2015-01-07 21:25 +0000 [r430355]  Kinsey Moore <kmoore at digium.com>
+
+	* res/res_stasis.c: ARI: Allow usage of ASYNCGOTO with Stasis()
+	  When the AMI Redirect action is used with a channel bridged
+	  inside Stasis() and not running a pbx, the channel is hung up
+	  instead of proceeding to the desired location in dialplan. This
+	  change allows such channels to be Redirected properly by
+	  detecting the operation used by Redirect (ASYNCGOTO) and using
+	  the code already established for functionality of the ARI channel
+	  continue operation. ASTERISK-24591 #close Review:
+	  https://reviewboard.asterisk.org/r/4271/
+
+2015-01-07 18:53 +0000 [r430337]  Mark Michelson <mmichelson at digium.com>
+
+	* rest-api/api-docs/channels.json, rest-api/resources.json,
+	  res/ari/resource_channels.c, CHANGES, res/res_ari_channels.c,
+	  res/ari/resource_channels.h: Add the ability to continue and
+	  originate using priority labels. With this patch, the following
+	  two ARI commands POST /channels POST /channels/{id}/continue
+	  Accept a new parameter, label, that can be used to continue to or
+	  originate to a priority label in the dialplan. Because this is
+	  adding a new parameter to ARI commands, the API version of ARI
+	  has been bumped from 1.6.0 to 1.7.0. This patch comes courtesy of
+	  Nir Simionovich from Greenfield Tech. Thanks! ASTERISK-24412
+	  #close Reported by Nir Simionovich Review:
+	  https://reviewboard.asterisk.org/r/4285
+
+2015-01-07 18:17 +0000 [r430315-430319]  George Joseph <george.joseph at fairview5.com>
+
+	* res/res_pjsip_exten_state.c: res_pjsip_exten_state: Change 'does
+	  not exist' warning to notice The 'new_subscribe: Extension <>
+	  does not exist or has no associated hint' is a config issue and
+	  doesn't need to clutter up logs with warnings. Changed to notice.
+	  Tested-by: George Joseph Review:
+	  https://reviewboard.asterisk.org/r/4307/
+
+	* res/res_pjsip_mwi.c: res_pjsip_mwi: Change "MWI Subscription
+	  failed" message from warning to notice The "MWI Subscription
+	  failed" message means the client is trying to subscribe to a
+	  mailbox that doesn't exist. There's no need to clutter up logs
+	  with warnings for a client misconfiguration so I changed it to a
+	  notice. Tested-by: George Joseph Review:
+	  https://reviewboard.asterisk.org/r/4306/
+
+	* funcs/func_config.c, tests/test_config.c: func_config: Add
+	  ability to retrieve specific occurrence of a variable I guess
+	  nobody uses templates with AST_CONFIG because today if you have a
+	  context that inherits from a template and you call AST_CONFIG on
+	  the context, you'll get the value from the template even if
+	  you've overridden it in the context. This is because AST_CONFIG
+	  only gets the first occurrence which is always from the template.
+	  This patch adds an optional 'index' parameter to AST_CONFIG which
+	  lets you specify the exact occurrence to retrieve, or '-1' to
+	  retrieve the last. The default behavior is the current behavior.
+	  Tested-by: George Joseph Review:
+	  https://reviewboard.asterisk.org/r/4313/
+
+2015-01-07 17:35 +0000 [r430313]  Mark Michelson <mmichelson at digium.com>
+
+	* res/res_pjsip_refer.c: Fix ability to perform a remote attended
+	  transfer with PJSIP. This fix has two parts: * Corrected an error
+	  message to properly state that external_replaces is an extension.
+	  The error message also prints what dialplan context the
+	  external_replaces extension was being looked for in. * Corrected
+	  the printing of the Replaces: header in an INVITE request. We
+	  were duplicating "Replaces: " in the header. ASTERISK-24376
+	  #close Reported by Matt Jordan Review:
+	  https://reviewboard.asterisk.org/r/4296
+
+2015-01-07 16:55 +0000 [r430295]  George Joseph <george.joseph at fairview5.com>
+
+	* include/asterisk/config.h, main/config.c, main/manager.c: config:
+	  Add option to NOT preserve effective context when changing a
+	  template Let's say you have a template T with variable VAR1 = ON
+	  and you have a context C(T) that doesn't specify VAR1. If you
+	  read C, the effective value of VAR1 is ON. Now you change T VAR1
+	  to OFF and call ast_config_text_file_save. The current behavior
+	  is that the file gets re-written with T/VAR1=OFF but C/VAR1=ON is
+	  added. Personally, I think this is a bug. It's preserving the
+	  effective state of C even though I didn't specify C/VAR1 in th
+	  first place. I believe the behavior should be that if I didn't
+	  specify C/VAR1 originally, then the effective value of C/VAR1
+	  should continue to follow the inherited state. Now, if I DID
+	  explicitly specify C/VAR1, the it should be preserved even if the
+	  template changes. Even though I think the existing behavior is a
+	  bug, it's been that way forever so I'm not changing it. Instead,
+	  I've created ast_config_text_file_save2() that takes a bitmask of
+	  flags, one of which is to preserve the effective context (the
+	  current behavior). The original ast_config_text_file_save calls
+	  *2 with the preserve flag. If you want the new behavior, call *2
+	  directly without a flag. I've also updated Manager UpdateConfig
+	  with a new parameter 'PreserveEffectiveContext' whose default is
+	  'yes'. If you want the new behavior with UpdateConfig, set
+	  'PreserveEffectiveContext: no'. Tested-by: George Joseph Review:
+	  https://reviewboard.asterisk.org/r/4297/
+
+2015-01-07 02:52 +0000 [r430274]  Kinsey Moore <kmoore at digium.com>
+
+	* res/res_pjsip/pjsip_options.c, res/res_pjsip.c,
+	  main/rtp_engine.c: Fix dev-mode build on recent gcc
+
+2015-01-06 22:46 +0000 [r430252]  Matthew Jordan <mjordan at digium.com>
+
+	* contrib/ast-db-manage/config/versions/371a3bf4143e_add_user_eq_phone_option_to_pjsip.py:
+	  contrib/ast-db-manage: Correct down_revision path for
+	  user_eq_phone When the user_eq_phone patch was backported to 13,
+	  it referenced the downward revision that the PJSIP optimistic
+	  encryption option also references. This creates a multi-path
+	  upgrade Exception when generating the SQL files. This patch
+	  corrects this in the 13 branch. Note that trunk, which already
+	  contained both of these features, is unaffected by this problem.
+
+2015-01-06 17:52 +0000 [r430221-430227]  George Joseph <george.joseph at fairview5.com>
+
+	* res/res_pjsip_mwi.c: res_pjsip_mwi: Change warning to notice When
+	  res_pjsip loads and an endpoint auto-subscribes a mailbox for
+	  mwi, if a contact hasn't registered yet, res_pjsip_mwi spits out
+	  a warning. This is a perfectly normal situation though and
+	  doesn't require something as serious as a warning. It's also self
+	  correcting. The device will start getting mwi as soon as it
+	  registers. This patch changes the warning to a notice. Tested-by:
+	  George Joseph Review: https://reviewboard.asterisk.org/r/4314/
+
+	* bridges/bridge_native_rtp.c: bridge_native_rtp: Change
+	  local/remote message from debug/2 to verb/4 Change the "Locally
+	  bridged"/"Remotely bridged" messages from dbg/2 to verb/4.
+	  Tested-by: George Joseph Review:
+	  https://reviewboard.asterisk.org/r/4300/
+
+	* res/res_pjsip_outbound_registration.c, CHANGES:
+	  outbound_registration: Add 'pjsip send register' and update 'send
+	  unregister' The current behavior of 'pjsip send unregister' is to
+	  send the unregister (REGISTER with 0 exp) but let the next
+	  scheduled register proceed normally. I don't think that's a good
+	  idea. If you unregister, it should stay unregistered until you
+	  decide to start registrations again. So this patch just adds a
+	  cancel_registration call to the current unregister_task to cancel
+	  the timer. Of course, now you need a way to start registration
+	  again so I've added a 'pjsip send register' command that
+	  unregisters and cancels any existing registration (the same as
+	  send unregister), then sends an immediate registration and starts
+	  the timer back up again. Both changes also ripple to AMI. There's
+	  a new PJSIPRegister command. There's no harm in calling either
+	  command repeatedly. They don't care about the actual state.
+	  Tested-by: George Joseph Review:
+	  https://reviewboard.asterisk.org/r/4301/
+
+	* res/res_pjsip/location.c: pjsip cli: Fix sorting of contacts for
+	  'pjsip list contacts' For some reason I was using a hash
+	  container instead of a list to gather the contacts for 'pjsip
+	  list/show contacts' so even though I had a sort function, the
+	  output wasn't sorted. This patch just changes the hash container
+	  to a list container and the contacts now appear sorted in the
+	  CLI. Tested-by: George Joseph Review:
+	  https://reviewboard.asterisk.org/r/4305/
+
+2015-01-05 22:49 +0000 [r430200]  Scott Griepentrog <sgriepentrog at digium.com>
+
+	* /, main/bridge_basic.c: bridge: avoid leaking channel during
+	  blond transfer pt2 A blond transfer to a failed destination, when
+	  followed by a recall attempt, lead to a leak of the reference to
+	  the destination channel. In addition to correcting the regression
+	  on the previous attempt (r429826) this fixes the leak and two
+	  additional reference leaks on failures of bridge_import.
+	  ASTERISK-24513 #close Review:
+	  https://reviewboard.asterisk.org/r/4302/ ........ Merged
+	  revisions 430199 from
+	  http://svn.asterisk.org/svn/asterisk/branches/12
+
+2015-01-05 17:56 +0000 [r430179-430181]  Joshua Colp <jcolp at digium.com>
+
+	* CHANGES: pjsip: Document addition of 'PJSIP_AOR' and
+	  'PJSIP_CONTACT' in CHANGES file.
+
+	* funcs/func_pjsip_contact.c (added), res/res_pjsip_session.c,
+	  include/asterisk/res_pjsip.h,
+	  channels/pjsip/dialplan_functions.c,
+	  include/asterisk/res_pjsip_session.h, funcs/func_pjsip_aor.c
+	  (added), res/res_pjsip/location.c: pjsip: Add 'PJSIP_AOR' and
+	  'PJSIP_CONTACT' dialplan functions. The PJSIP_AOR dialplan
+	  function allows inspection of configured AORs including what
+	  contacts are currently bound to them. The PJSIP_CONTACT dialplan
+	  function allows inspection of contacts in existence. These can
+	  include both externally added (by way of registration) or
+	  permanent ones. ASTERISK-24341 Reported by: xrobau Review:
+	  https://reviewboard.asterisk.org/r/4308/
+
+2014-12-29 13:10 +0000 [r430145]  Kinsey Moore <kmoore at digium.com>
+
+	* res/res_pjsip.c: PJSIP: Update transport method documentation
+	  This updates the documentation for the 'method' configuration
+	  option to be more verbose about the behaviors of values
+	  'unspecified' and 'default'. They do exactly the same thing which
+	  is to select the default as defined by PJSIP which is currently
+	  TLSv1. Review: https://reviewboard.asterisk.org/r/4264/
+
+2014-12-24 21:27 +0000 [r430127]  Kevin Harwell <kharwell at digium.com>
+
+	* /, configs/samples/queues.conf.sample: app_queue: Update sample
+	  conf documenation Updated the queues.conf.sample file to
+	  explicitly state which channel queue variables are propagated to.
+	  ASTERISK-24267 Reported by: Mitch Claborn ........ Merged
+	  revisions 430126 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2014-12-24 15:26 +0000 [r430083-430092]  Matthew Jordan <mjordan at digium.com>
+
+	* res/res_pjsip.c,
+	  contrib/ast-db-manage/config/versions/371a3bf4143e_add_user_eq_phone_option_to_pjsip.py
+	  (added): res_pjsip: Backport missing commits for user_eq_phone
+	  This backports the following from trunk, which were missed:
+	  r427257 | file | 2014-11-04 16:31:16 -0600 (Tue, 04 Nov 2014) | 2
+	  lines res_pjsip: Allow + at the beginning of a phone number when
+	  user_eq_phone is enabled. r427259 | file | 2014-11-04 16:51:32
+	  -0600 (Tue, 04 Nov 2014) | 2 lines res_pjsip: Apply the
+	  'user_eq_phone' setting to the To header as well. It also adds
+	  the Alembic script for the option. ASTERISK-24643
+
+	* CHANGES, res/res_pjsip.c, include/asterisk/res_pjsip.h,
+	  res/res_pjsip/config_global.c, res/res_pjsip_keepalive.c (added),
+	  configs/samples/pjsip.conf.sample: res_pjsip_keepalive: Add
+	  runtime configurable keepalive module for connection-oriented
+	  transports. Note that this is backport from trunk of r425825.
+	  This change adds a module which is configurable using the
+	  keep_alive_interval setting in the global section that will send
+	  a CRLF keep alive to all active connection-oriented transports at
+	  the provided interval. This is useful because it can help keep
+	  connections open through NATs. This functionality also exists
+	  within PJSIP but can not be controlled at runtime and requires
+	  recompiling it. Review: https://reviewboard.asterisk.org/r/4084/
+	  ASTERISK-24644 #close
+
+	* res/res_pjsip/pjsip_configuration.c, res/res_pjsip_caller_id.c,
+	  CHANGES, res/res_pjsip.c, include/asterisk/res_pjsip.h:
+	  res_pjsip: Add 'user_eq_phone' option to add a 'user=phone'
+	  parameter when applicable. Note that this is a backport of
+	  r425804 from trunk. This change adds a configuration option which
+	  adds a 'user=phone' parameter if the user portion of the request
+	  URI or the From URI is determined to be a number. Review:
+	  https://reviewboard.asterisk.org/r/4073/ ASTERISK-24643 #close
+
+2014-12-23 23:18 +0000 [r430059-430064]  George Joseph <george.joseph at fairview5.com>
+
+	* res/res_pjsip/pjsip_options.c: pjsip_options: Fix continued
+	  qualifies after endpoint/aor deletion If you remove an
+	  endpoint/aor from pjsip.conf then do a core reload, qualifies
+	  will continue even though the object are gone. This happens
+	  because nothing clears out the qualify tasks. This patch
+	  unschedules all existing qualify tasks before scheduling new ones
+	  on reload. Tested-by: George Joseph Review:
+	  https://reviewboard.asterisk.org/r/4290/
+
+	* tests/test_astobj2.c: test_astobj2: Fix warning for missing
+	  trailing slash in category This patch adds a trailing slash to
+	  the category for this test. No more warning. Tested-by: George
+	  Joseph Review: https://reviewboard.asterisk.org/r/4295/
+
+2014-12-22 21:18 +0000 [r430010-430034]  Richard Mudgett <rmudgett at digium.com>
+
+	* main/bridge_basic.c: DTMF atxfer: Setup recall channels as if the
+	  transferee initiated the call. After the initial DTMF atxfer call
+	  attempt to the transfer target fails to answer during a blonde
+	  transfer, the recall callback channels do not get setup with
+	  information from the initial transferrer channel. As a result,
+	  the recall callback to the transferrer does not have callid,
+	  channel variables, datastores, accountcode, peeraccount, COLP,
+	  and CLID setup. A similar situation happens with the recall
+	  callback to the transfer target but it is less visible. The
+	  recall callback to the transfer target does not have callid,
+	  channel variables, datastores, accountcode, peeraccount, and COLP
+	  setup. * Added missing information to the recall callback
+	  channels before initiating the call. callid, channel variables,
+	  datastores, accountcode, peeraccount, COLP, and CLID * Set callid
+	  of the transferrer channel on the DTMF atxfer controller thread
+	  attended_transfer_monitor_thread(). * Added missing channel
+	  unlocks and props unref to off nominal paths in
+	  attended_transfer_properties_alloc(). ASTERISK-23841 #close
+	  Reported by: Richard Mudgett Review:
+	  https://reviewboard.asterisk.org/r/4259/
+
+	* /, main/logger.c, include/asterisk/_private.h, main/asterisk.c:
+	  queue_log: Post QUEUESTART entry when Asterisk fully boots. The
+	  QUEUESTART log entry has historically acted like a fully booted
+	  event for the queue_log file. When the QUEUESTART entry was
+	  posted to the log was broken by the change made by
+	  ASTERISK-15863. * Made post the QUEUESTART queue_log entry when
+	  Asterisk fully boots. This restores the intent of that log entry
+	  and happens after realtime has had a chance to load. AST-1444
+	  #close Reported by: Denis Martinez Review:
+	  https://reviewboard.asterisk.org/r/4282/ ........ Merged
+	  revisions 430009 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2014-12-22 15:40 +0000 [r429983]  Matthew Jordan <mjordan at digium.com>
+
+	* /, channels/chan_sip.c: chan_sip: Send CANCEL via original INVITE
+	  destination even after UPDATE request Given the following
+	  scenario: * Three SIP phones (A, B, C), all communicating via a
+	  proxy with Asterisk * A call is established between A and B. B
+	  performs a SIP attended transfer of A to C. B sets the call on
+	  hold (A is hearing MOH) and dials the extension of C. While phone
+	  C is ringing, B transfers the call (that is, what we typically
+	  call a 'blond transfer'). * When the transfer completes, A hears
+	  the ringing of phone C, while B is idle. In the SIP messaging for
+	  the above scenario, a REFER request is sent to transfer the call.
+	  When "sendrpid=yes" is set in sip.conf, Asterisk may send an
+	  UPDATE request to phone C to update party information. This
+	  update is sent directly to phone C, not through the intervening
+	  proxy. This has the unfortunate side effect of providing route
+	  information, which is then set on the sip_pvt structure for C. If
+	  someone (e.g. B) is trying to get the call back (through a
+	  directed pickup), Asterisk will send a CANCEL request to C.
+	  However, since we have now updated the route set, the CANCEL
+	  request will be sent directly to C and not through the proxy. The
+	  phone ignores this CANCEL according to RFC3261 (Section 9.1).
+	  This patch updates reqprep such that the route is not updated if
+	  an UPDATE request is being sent while the INVITE state is
+	  INV_PROCEEDING or INV_EARLY_MEDIA. This ensures that a subsequent
+	  CANCEL request is still sent to the correct location. Review:
+	  https://reviewboard.asterisk.org/r/4279 ASTERISK-24628 #close
+	  Reported by: Karsten Wemheuer patches: issue.patch uploaded by
+	  Karsten Wemheuer (License 5930) ........ Merged revisions 429982
+	  from http://svn.asterisk.org/svn/asterisk/branches/11
+
+2014-12-22 00:17 +0000 [r429914]  George Joseph <george.joseph at fairview5.com>
+
+	* res/res_pjsip_phoneprov_provider.c:
+	  res_pjsip_phoneprovi_provider: Fix reload Reloading wasn't
+	  working correctly because on a reload, the sorcery apply handler
+	  was never being called for unchanged users. So, instead of using
+	  an apply handler, I'm now iterating over all users. Works much
+	  more reliably. Tested-by: George Joseph Review:
+	  https://reviewboard.asterisk.org/r/4288/
+
+2014-12-20 20:57 +0000 [r429894]  Joshua Colp <jcolp at digium.com>
+
+	* main/named_acl.c, /: acl: Fix reloading of configuration if
+	  configuration file does not exist at startup. The named ACL code
+	  incorrectly destroyed the config options information if loading
+	  of the configuration file failed at startup. This would result in
+	  reloading also failing even if a valid configuration file was put
+	  in place. ASTERISK-23733 #close Reported by: Richard Kenner
+	  ........ Merged revisions 429893 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2014-12-19 20:54 +0000 [r429829-429868]  Richard Mudgett <rmudgett at digium.com>
+
+	* /, res/res_http_websocket.c: res_http_websocket.c: Fix incorrect
+	  use of sizeof in ast_websocket_write(). This won't fix the
+	  reported issue but it is an incorrect use of sizeof.
+	  ASTERISK-24566 Reported by: Badalian Vyacheslav ........ Merged
+	  revisions 429867 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+	* channels/chan_dahdi.c, /: chan_dahdi: Don't ignore setvar when
+	  using configuration section scheme. When the configuration
+	  section scheme of chan_dahdi.conf is used (keyword dahdichan
+	  instead of channel) all setvar= options are completely ignored.
+	  No variable defined this way appears in the created DAHDI
+	  channels. * Move the clearing of setvar values to after the
+	  deferred processing of dahdichan. AST-1378 #close Reported by:
+	  Guenther Kelleter Patch by: Guenther Kelleter ........ Merged
+	  revisions 429825 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2014-12-19 17:26 +0000 [r429827]  Scott Griepentrog <sgriepentrog at digium.com>
+
+	* /, main/bridge_basic.c: bridge: avoid leaking channel during
+	  blond transfer After a blond transfer (start attended and hang
+	  up) to a destination that also hangs up without answer, the
+	  Local;1 channel was leaked and would show up on core show
+	  channels. This was happening because the attended state
+	  blond_nonfinal_enter() resetting the props->transfer_target to
+	  null while releasing it's own reference, which would later
+	  prevent props from releasing another reference during
+	  destruction. The change made here is simply to not assign the
+	  target to NULL. ASTERISK-24513 #close Reported by: Mark Michelson
+	  Review: https://reviewboard.asterisk.org/r/4262/ ........ Merged
+	  revisions 429826 from
+	  http://svn.asterisk.org/svn/asterisk/branches/12
+
+2014-12-18 22:38 +0000 [r429784-429805]  Richard Mudgett <rmudgett at digium.com>
+
+	* res/res_rtp_asterisk.c, channels/chan_dahdi.c, /: chan_dahdi.c,
+	  res_rtp_asterisk.c: Change some spammy debug messages to level 5.
+	  ASTERISK-24337 #close Reported by: Rusty Newton ........ Merged
+	  revisions 429804 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+	* UPGRADE.txt, channels/sig_analog.c, /: chan_dahdi: Populate
+	  CALLERID(ani2) for incoming calls in featdmf signaling mode. For
+	  the featdmf signaling mode the incoming MF Caller-ID information
+	  is formatted as follows:
+	  *${CALLERID(ani2)}${CALLERID(ani)}#*${EXTEN}# Rather than
+	  discarding the ani2 digits, populate the CALLERID(ani2) value
+	  with what is received instead. AST-1368 #close Reported by: Denis
+	  Martinez Patches: extract_ani2_for_featdmf_v11.patch (license
+	  #5621) patch uploaded by Richard Mudgett ........ Merged
+	  revisions 429783 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2014-12-18 15:50 +0000 [r429763]  Kevin Harwell <kharwell at digium.com>
+
+	* res/res_pjsip_sdp_rtp.c: res_pjsip_sdp_rtp: wrong bridge chosen
+	  when the DTMF mode is not compatible A native rtp bridge was
+	  being chosen (it shouldn't have been) when using two pjsip
+	  channels with incompatible DTMF modes. This patch sets the rtp
+	  instance property, AST_RTP_PROPERTY_DTMF, for the appropriate
+	  DTMF mode(s) for pjsip. It was not being set before, meaning all
+	  DTMF modes for pjsip were being treated as compatible, thus
+	  native bridging would be chosen as the bridge type when it
+	  shouldn't have been. ASTERISK-24459 #close Reported by: Yaniv
+	  Simhi Review: https://reviewboard.asterisk.org/r/4265/
+
+2014-12-18 15:34 +0000 [r429739-429761]  Mark Michelson <mmichelson at digium.com>
+
+	* res/res_pjsip_outbound_registration.c: Prevent potential infinite
+	  outbound authentication loops in registration. Prior to this
+	  patch, Asterisk would always respond to 401 responses to
+	  registration attempts by trying to provide a registration with
+	  authentication credentials. Even if subsequent attempts were
+	  rejected with 401 responses, Asterisk would continue this
+	  behavior. If authentication credentials were incorrect, this
+	  could continue forever. With this patch, we keep track of whether
+	  we have attempted authentication on an outbound registration
+	  attempt. If we already have, we don not try again until the next
+	  attempt. This prevents the infinite loop scenario. Review:
+	  https://reviewboard.asterisk.org/r/4273
+
+	* main/manager.c: Prevent possible race condition on dual redirect
+	  of channels in the same bridge. The
+	  AST_FLAG_BRIDGE_DUAL_REDIRECT_WAIT flag was created to prevent
+	  bridges from prematurely acting on orphaned channels in bridges.
+	  The problem with the AMI redirect action was that it was setting
+	  this flag on channels based on the presence of a PBX, not whether
+	  the channel was in a bridge. Whether a channel has a PBX is
+	  irrelevant, so the condition has been altered to check if the
+	  channel is in a bridge. ASTERISK-24536 #close Reported by Niklas
+	  Larsson Review: https://reviewboard.asterisk.org/r/4268
+
+	* channels/pjsip/dialplan_functions.c: Ensure the correct value is
+	  returned for CHANNEL(pjsip, secure) Prior to this patch, we were
+	  using the PJSIP dialog's secure flag to determine if a secure
+	  transport was being used. Unfortunately, the dialog's secure flag
+	  was only set if a SIPS URI were in use, as required by RFC 3261
+	  sections 12.1.1 and 12.1.2. What we're interested in is not
+	  dialog security, but transport security. This code change
+	  switches to a model where we use the dialog's target URI to
+	  determine what transport would be used to communicate, and then
+	  check if that transport is secure. AST-1450 #close Reported by
+	  John Bigelow Review: https://reviewboard.asterisk.org/r/4277
+
+2014-12-18 00:10 +0000 [r429699-429719]  George Joseph <george.joseph at fairview5.com>
+
+	* res/res_pjsip_config_wizard.c: res_pjsip_config_wizard: fix
+	  unload SEGV If certain pjsip modules aren't loaded, the wizard
+	  causes a SEGV when it unloads. Added a check for the presense of
+	  the object type wizard before trying to clean it up. Tested-by:
+	  George Joseph
+
+	* res/res_pjsip_config_wizard.c: res_pjsip_config_wizard: Change
+	  FILEUNCHANGED config_load2 flag determination The module now
+	  applies the FILEUNCHANGED flag when both reloaded is specified
+	  AND there's no last_config for the object type. Tested-by: George
+	  Joseph Review: https://reviewboard.asterisk.org/r/4276/
+
+2014-12-17 09:54 +0000 [r429675]  Walter Doekes <walter+asterisk at wjd.nu>
+
+	* addons/ooh323c/src/printHandler.c, apps/app_adsiprog.c,
+	  channels/chan_unistim.c, main/udptl.c, res/res_rtp_asterisk.c, /,
+	  channels/chan_sip.c, channels/vcodecs.c, res/res_crypto.c,
+	  utils/astman.c, utils/smsq.c, main/utils.c, pbx/dundi-parser.c,
+	  apps/app_getcpeid.c, channels/chan_iax2.c, channels/sig_pri.c,
+	  res/res_pktccops.c, main/loader.c, channels/iax2/parser.c,
+	  main/uuid.c, main/manager.c, channels/chan_misdn.c,
+	  apps/app_osplookup.c, channels/misdn/ie.c, main/http.c,
+	  apps/app_sms.c: Fix printf problems with high ascii characters
+	  after r413586 (1.8). In r413586 (1.8) various casts were added to
+	  silence gcc 4.10 warnings. Those fixes included things like: -out
+	  += sprintf(out, "%%%02X", (unsigned char) *ptr); +out +=
+	  sprintf(out, "%%%02X", (unsigned) *ptr); That works for low ascii
+	  characters, but for the high range that yields e.g. FFFFFFC3 when
+	  C3 is expected. This changeset: - fixes those casts to use the
+	  'hh' unsigned char modifier instead - consistently uses %02x
+	  instead of %2.2x (or other non-standard usage) - adds a few 'h'
+	  modifiers in various places - fixes a 'replcaes' typo -
+	  dev/urandon typo (in 13+ patch) Review:
+	  https://reviewboard.asterisk.org/r/4263/ ASTERISK-24619 #close
+	  Reported by: Stefan27 (on IRC) ........ Merged revisions 429673
+	  from http://svn.asterisk.org/svn/asterisk/branches/11 ........
+	  Merged revisions 429674 from
+	  http://svn.asterisk.org/svn/asterisk/branches/12
+
+2014-12-16 17:53 +0000 [r429653]  George Joseph <george.joseph at fairview5.com>
+
+	* res/res_pjsip_config_wizard.c: res_pjsip_config_wizard: fix test
+	  breakage Fix test breakage caused by not checking for res_pjsip
+	  before calling ast_sip_get_sorcery. Tested-by: George Joseph
+	  Review: https://reviewboard.asterisk.org/r/4269/
+
+2014-12-16 16:38 +0000 [r429612-429633]  Joshua Colp <jcolp at digium.com>
+
+	* /, channels/chan_sip.c: chan_sip: Allow T.38 switch-over when
+	  SRTP is in use. Previously when SRTP was enabled on a channel it
+	  was not possible to switch to T.38 as no crypto attributes would
+	  be present. This change makes it so it is now possible. If a T.38
+	  re-invite comes in SRTP is terminated since in practice you can't
+	  encrypt a UDPTL stream. Now... if we were doing T.38 over RTP
+	  (which does exist) then we'd have a chance but almost nobody does
+	  that so here we are. ASTERISK-24449 #close Reported by: Andreas
+	  Steinmetz patches: udptl-ignore-srtp-v2.patch submitted by
+	  Andreas Steinmetz (license 6523) ........ Merged revisions 429632
+	  from http://svn.asterisk.org/svn/asterisk/branches/11
+
+	* res/res_pjsip_t38.c: res_pjsip_t38: Fix T.38 failure when peer
+	  reinvites immediately. If a remote endpoint reinvites to T.38
+	  immediately the state machine will go into a peer reinvite state.
+	  If a T.38 capable application (such as ReceiveFax) queries it
+	  will receive this state. Normally the application will then
+	  indicate so that the channel driver will queue up the T.38 offer
+	  previously received. Once it receives this offer the application
+	  will act normally and negotiate. The res_pjsip_t38 module
+	  incorrectly partially squashed this indication. This would cause
+	  the application to think the request had failed when in reality
+	  it had actually worked. This change makes it so that no T.38
+	  control frames (or indications) are squashed.
+
+2014-12-15 17:07 +0000 [r429592]  George Joseph <george.joseph at fairview5.com>
+
+	* res/res_pjsip_phoneprov_provider.c,
+	  configs/samples/pjsip_wizard.conf.sample (added), CHANGES,
+	  res/res_pjsip_config_wizard.c (added): res_pjsip_config_wizard:
+	  Allow streamlined config of common pjsip scenarios
+	  res_pjsip_config_wizard ------------------ * This is a new module
+	  that adds streamlined configuration capability for chan_pjsip.
+	  It's targetted at users who have lots of basic configuration
+	  scenarios like 'phone' or 'agent' or 'trunk'. Additional
+	  information can be found in the sample configuration file at
+	  config/samples/pjsip_wizard.conf.sample. Tested-by: George Joseph
+	  Review: https://reviewboard.asterisk.org/r/4190/
+
+2014-12-15 15:36 +0000 [r429571]  Mark Michelson <mmichelson at digium.com>
+
+	* res/res_pjsip_pubsub.c: Activate persistent subscriptions when
+	  they are recreated. Prior to this change, recreating persistent
+	  subscriptions would create the subscription but would not
+	  activate it. This led to subscriptions being listed in the "NULL"
+	  state by diagnostics and not sending NOTIFYs when expected.
+	  Review: https://reviewboard.asterisk.org/r/4261
+
+2014-12-12 23:54 +0000 [r429542]  George Joseph <george.joseph at fairview5.com>
+
+	* main/manager.c, include/asterisk/module.h,
+	  include/asterisk/_private.h: loader: Move definition of
+	  ast_module_reload from _private.h to module.h No functionality
+	  change. Just move the definition of ast_module_reload from
+	  _private.h to module.h so it can be public. Also removed the
+	  include of _private.h from manager.c since ast_module_load was
+	  the only reason for including it. Tested-by: George Joseph
+	  Review: https://reviewboard.asterisk.org/r/4251/
+
+2014-12-12 23:40 +0000 [r429540]  Richard Mudgett <rmudgett at digium.com>
+
+	* main/lock.c, /, include/asterisk/lock.h: DEBUG_THREADS: Fix
+	  regression and lock tracking initialization problems. This patch
+	  started with David Lee's patch at
+	  https://reviewboard.asterisk.org/r/2826/ and includes a
+	  regression fix introduced by the ASTERISK-22455 patch. The
+	  initialization of a mutex's lock tracking structure was not
+	  protected in a critical section. This is fine for any mutex that
+	  is explicitly initialized, but a static mutex may have its lock
+	  tracking double initialized if multiple threads attempt the first
+	  lock simultaneously. * Added a global mutex to properly serialize
+	  initialization of the lock tracking structure. The painful global
+	  lock can be mitigated by adding a double checked lock flag as
+	  discussed on the original review request. * Defer lock tracking
+	  initialization until first use. * Don't be "helpful" and
+	  initialize an uninitialized lock when DEBUG_THREADS is enabled.
+	  Debug code is not supposed to fix or change normal code behavior.
+	  We don't need a lock initialization race that would force a
+	  re-setup of lock tracking. Lock tracking already handles
+	  initialization on first use. * Properly handle allocation
+	  failures of the lock tracking structure. * No need to initialize
+	  tracking data in __ast_pthread_mutex_destroy() just to turn
+	  around and destroy it. The regression introduced by
+	  ASTERISK-22455 is the result of manipulating a pthread_mutex_t
+	  struct outside of the pthread library code. The pthread_mutex_t
+	  struct seems to have a global linked list pointer member that can
+	  get changed by other threads. Therefore, saving and restoring the
+	  contents of a pthread_mutex_t struct is a bad thing. Thanks to
+	  Thomas Airmont for finding this obscure regression. * Don't
+	  overwrite the struct ast_lock_track.reentr_mutex member to
+	  restore tracking data in __ast_cond_wait() and
+	  __ast_cond_timedwait(). The pthread_mutex_t struct must be
+	  treated as a read-only opaque variable. Miscellaneous other items
+	  fixed by this patch: * Match ast_suspend_lock_info() with
+	  ast_restore_lock_info() in __ast_cond_timedwait(). * Made some
+	  uninitialized lock sanity checks return EINVAL and try a
+	  DO_THREAD_CRASH. * Fix bad canlog initialization expressions.
+	  ASTERISK-24614 #close Reported by: Thomas Airmont Review:
+	  https://reviewboard.asterisk.org/r/4247/ Review:
+	  https://reviewboard.asterisk.org/r/2826/ ........ Merged
+	  revisions 429539 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11
+
+2014-12-12 22:53 +0000 [r429518-429519]  Matthew Jordan <mjordan at digium.com>
+
+	* res/res_agi.c: res/res_agi: Make Verbose message for 'stream
+	  file' match other playbacks The Verbose message displayed when a
+	  file is played back via 'stream file' was formatted differently
+	  than other playbacks: * It didn't include the channel name * It
+	  didn't include the channel language It does, however, include the
+	  playback offset as well as any escape digits. That information
+	  was kept; however, this patch updates the formatting to more
+	  closely match the Verbose messages displayed when a file is
+	  played back by 'control stream file', Playback, ControlPlayback,
+	  or any other file playback operation.
+
+	* /: Add 11 merge properties
+
+2014-12-12 16:57 +0000 [r429497]  Joshua Colp <jcolp at digium.com>
+
+	* main/format.c, main/codec.c, include/asterisk/format.h: media:
+	  Fix crash when determining sample count of a frame during
+	  shutdown. When shutting down Asterisk the codecs are cleaned up.
+	  As a result anything attempting to get a codec based on ID or
+	  details will find that no codec exists. This currently occurs
+	  when determining the sample count of a frame. This code did not
+	  take this situation into account. This change fixes this by
+	  getting the codec directly from the format and eliminates the
+	  lookup. This is both faster and also provides a guarantee that
+	  the codec will exist and will be valid. ASTERISK-24604 #close
+	  Reported by: Matt Jordan Review:
+	  https://reviewboard.asterisk.org/r/4260/
+
+2014-12-12 15:30 +0000 [r429477]  Kevin Harwell <kharwell at digium.com>
+
+	* channels/chan_pjsip.c: chan_pjsip: Race between channel answer
+	  and bridge setup when using direct media When direct media is
+	  enabled and a pjsip channel is answered a race would occur
+	  between the handling of the answer and bridge setup. Sometimes
+	  the media negotiation would take place after the native bridge
+	  was setup. This resulted in a NULL media address, which in turn
+	  resulted in Asterisk using its address as the remote media
+	  address when sending a reinvite. This patch makes the chan_pjsip
+	  answer handler synchronous thus alleviating the race condition
+	  (the bridge won't start setting things up until after it
+	  returns). ASTERISK-24563 #close Reported by: Steve Pitts Review:
+	  https://reviewboard.asterisk.org/r/4257/
+
+2014-12-12 15:00 +0000 [r429457]  David M. Lee <dlee at digium.com>
+
+	* res/res_pjsip_outbound_publish.c: Fix crash for sorcery
+	  misconfigs res_pjsip_outbound_publish was missing the
+	  CHECK_PJSIP_MODULE_LOADED() call in load_module, and would crash
+	  with a segfault if res_pjsip declined to load. Review:
+	  https://reviewboard.asterisk.org/r/4258/
+
+2014-12-12 14:12 +0000 [r429430-429433]  Kinsey Moore <kmoore at digium.com>
+
+	* /, res/res_pjsip_sdp_rtp.c: PJSIP: Allow use of 'inactive'
+	  streams for hold This allows use of the 'inactive' stream
+	  direction identifier to be used for hold where 'sendonly' is
+	  normally used. Some Seimens phones use 'inactive' and this change
+	  allows music on hold to operate properly. Review:
+	  https://reviewboard.asterisk.org/r/4252/ Reported by: Steve Pitts
+	  ........ Merged revisions 429432 from
+	  http://svn.asterisk.org/svn/asterisk/branches/12
+
+	* res/res_sorcery_config.c, /: Sorcery: Log when old config remains
+	  in use This adds a log message notifying the user that a stale
+	  configuration is in place upon reload when a config object fails
+	  to load. This situation can end up causing confusion when the
+	  object failed to load but exists from a previous config load
+	  especially when the old config is significantly different from
+	  the new config. Review: https://reviewboard.asterisk.org/r/4250/
+	  Reported by: Thomas Thompson ........ Merged revisions 429429
+	  from http://svn.asterisk.org/svn/asterisk/branches/12
+
+2014-12-12 13:05 +0000 [r429407-429409]  Joshua Colp <jcolp at digium.com>
+
+	* res/res_pjsip_session.exports.in, channels/chan_pjsip.c,
+	  res/res_pjsip_session.c, include/asterisk/res_pjsip_session.h:
+	  res_pjsip_session: Delay sending BYE if a re-INVITE transaction
+	  is in progress. Given the scenario where a PJSIP channel is in a
+	  native RTP bridge with direct media and the channel is then hung
+	  up the code will currently re-INVITE the channel back to Asterisk
+	  and send a BYE at the same time. Many SIP implementations dislike
+	  this greatly. This change makes it so that if a re-INVITE
+	  transaction is in progress the BYE is queued to occur after the
+	  completion of the transaction (be it through normal means or a
+	  timeout). Review: https://reviewboard.asterisk.org/r/4248/
+
+	* res/res_pjsip_session.c: res_pjsip_session: Fix issue where a
+	  declined media stream in a re-INVITE would fail SDP negotiation.
+	  In the past the SDP negotiation within res_pjsip_session was made
+	  more tolerant of certain situations. The only case where SDP
+	  negotiation will fail is when a major error occurs during
+	  negotiation. Receiving an already declined media stream is not
+	  considered a major error. When producing the local SDP the logic
+	  took this into account so on the initial INVITE the declined
+	  media stream did not cause an SDP negotiation failure.
+	  Unfortunately the logic for handling media streams with a handler
+	  did not mirror this logic and considered an already declined
+	  media stream an error and thus failed the SDP negotiation. This
+	  change makes the logic between both situations match so only
+	  under major errors will the SDP negotiation fail. ASTERISK-24607
+	  #close Reported by: Matt Jordan Review:
+	  https://reviewboard.asterisk.org/r/4254/
+
+2014-12-11 20:31 +0000 [r429387]  Kevin Harwell <kharwell at digium.com>
+
+	* CHANGES: ARI/AMI: Include language in standard channel snapshot
+	  output The CHANGES verbiage for the "language" addition had been
+	  put under the wrong release. This moves it to be under 13.1 to
+	  13.2 changes. ASTERISK-24553 Reported by: Matt Jordan
+
+2014-12-11 17:21 +0000 [r429352-429379]  Kinsey Moore <kmoore at digium.com>
+
+	* /: Recorded merge of revisions 429378 from
+	  http://svn.asterisk.org/svn/asterisk/branches/12 ........ Fix
+	  incorrect patch applied in r429354 The patch that was applied was
+	  another pending patch. This swaps them out.
+
+	* /: Recorded merge of revisions 429354 from
+	  http://svn.asterisk.org/svn/asterisk/branches/12 ........ Stasis:
+	  Update unittest for channel snapshots This adjusts the unit test
+	  for channel snapshots to take the new language key into account.
+
+	* tests/test_stasis_channels.c: Stasis: Update unittest for channel
+	  snapshots This adjusts the unit test for channel snapshots to
+	  take the new language key into account.
+
+2014-12-10 15:42 +0000 [r429326]  Kevin Harwell <kharwell at digium.com>
+
+	* /, CHANGES: ARI/AMI: Include language in standard channel
+	  snapshot output Adding information about including "language" in
+	  the standard channel snapshot output to the CHANGES file. Note
+	  the actual source changes have already been previously committed.
+	  ASTERISK-24553 Reported by: Matt Jordan ........ Merged revisions
+	  429325 from http://svn.asterisk.org/svn/asterisk/branches/12
+
+2014-12-10 13:34 +0000 [r429273]  Joshua Colp <jcolp at digium.com>
+
+	* res/res_http_websocket.c, res/res_pjsip_transport_websocket.c, /,
+	  channels/chan_sip.c: res_http_websocket: Fix crash due to double
+	  freeing memory when receiving a payload length of zero. Frames
+	  with a payload length of 0 were incorrectly handled in
+	  res_http_websocket. Provided a frame with a payload had been
+	  received prior it was possible for a double free to occur. The
+	  realloc operation would succeed (thus freeing the payload) but be
+	  treated as an error. When the session was then torn down the
+	  payload would be freed again causing a crash. The read function
+	  now takes this into account. This change also fixes assumptions
+	  made by users of res_http_websocket. There is no guarantee that a
+	  frame received from it will be NULL terminated. ASTERISK-24472
+	  #close Reported by: Badalian Vyacheslav Review:
+	  https://reviewboard.asterisk.org/r/4220/ Review:
+	  https://reviewboard.asterisk.org/r/4219/ ........ Merged
+	  revisions 429270 from
+	  http://svn.asterisk.org/svn/asterisk/branches/11 ........ Merged
+	  revisions 429272 from
+	  http://svn.asterisk.org/svn/asterisk/branches/12
+
+2014-12-10 13:14 +0000 [r429246]  Kinsey Moore <kmoore at digium.com>
+
+	* /, res/res_pjsip/pjsip_options.c: PJSIP: Fix assert on initial
+	  mass qualify This fixes the MWI test regressions caused by
+	  r429127 and ensures that contacts have non-zero qualify_frequency
+	  before attempting scheduling. ........ Merged revisions 429245
+	  from http://svn.asterisk.org/svn/asterisk/branches/12
+
+2014-12-09 20:46 +0000 [r429223]  Scott Griepentrog <sgriepentrog at digium.com>
+
+	* main/asterisk.c: core: avoid possible asterisk -r crash from long
+	  id When connecting to the remote console, an id string is first
+	  provided that consts of the hostname, pid, and version. This is
+	  parsed by the remote instance using a buffer that may be too
+	  short, and can allow a buffer overrun because it is not
+	  terminated. This patch adds termination and a larger buffer.
+	  Review: https://reviewboard.asterisk.org/r/4182/
+
+2014-12-09 20:19 +0000 [r429175-429206]  Kevin Harwell <kharwell at digium.com>
+
+	* res/ari/ari_model_validators.h, /, main/stasis_channels.c,
+	  rest-api/api-docs/channels.json, res/ari/ari_model_validators.c,
+	  main/manager_channels.c: ARI/AMI: Include language in standard
+	  channel snapshot output The channel "language" was already part
+	  of a channel snapshot, however is was not sent out over AMI or
+	  ARI. This patch makes it so the channel "language" is included in
+	  the appropriate AMI or ARI events. ASTERISK-24553 #close Reported
+	  by: Matt Jordan Review: https://reviewboard.asterisk.org/r/4245/
+	  ........ Merged revisions 429204 from
+	  http://svn.asterisk.org/svn/asterisk/branches/12
+
+	* include/asterisk/rtp_engine.h, res/res_rtp_asterisk.c,
+	  main/rtp_engine.c, /, channels/chan_sip.c: Direct Media calls
+	  within private network sometimes get one way audio When endpoints
+	  with direct_media enabled, behind a firewall (Asterisk on a
+	  separate network) and were bridged sometimes Asterisk would send
+	  the ip address of the firewall in the sdp to one of the phones in
+	  the reinvite resulting in one way audio. When sending the
+	  reinvite Asterisk will retrieve the media address from the
+	  associated rtp instance, but if frames were being read this can
+	  be overwritten with another address (in this case the
+	  firewall's). This patch ensures that Asterisk uses the original
+	  device address when using direct media. ASTERISK-24563 Reported
+	  by: Steve Pitts Review: https://reviewboard.asterisk.org/r/4216/
+	  ........ Merged revisions 429195 from
+	  http://svn.asterisk.org/svn/asterisk/branches/12
+
+	* res/res_pjsip_outbound_publish.c: res_pjsip_outbound_publish:
+	  stack overflow when using non-default sorcery wizard When using a
+	  non-default sorcery wizard (in this instance realtime) for
+	  outbound publishes Asterisk will crash after a stack overflow
+	  occurs due to the code infinitely recursing. The fix entails
+	  removing the outbound publish state dependency from the outbound
+	  publish sorcery object and instead keeping an in memory container
+	  that can be used to lookup the state when needed. ASTERISK-24514
+	  #close Reported by: Mark Michelson Review:
+	  https://reviewboard.asterisk.org/r/4178/
+
+2014-12-09 15:44 +0000 [r429153]  Joshua Colp <jcolp at digium.com>
+
+	* res/ari/resource_channels.h, rest-api/api-docs/channels.json,
+	  res/ari/resource_channels.c, CHANGES, res/res_ari_channels.c:
+	  ari: Add support for specifying an originator channel when
+	  originating. If an originator channel is specified when
+	  originating a channel the linked ID of it will be applied to the
+	  newly originated outgoing channel. This allows an association to
+	  be made between the two so it is known that the originator has
+	  dialed the originated channel. ASTERISK-24552 #close Reported by:
+	  Matt Jordan Review: https://reviewboard.asterisk.org/r/4243/
+
+2014-12-09 14:00 +0000 [r429128]  Kinsey Moore <kmoore at digium.com>
+
+	* /, res/res_pjsip/pjsip_options.c: PJSIP: Stagger outbound
+	  qualifies This change staggers initiation of outbound qualify
+	  (OPTIONS) attempts to reduce instantaneous server load and
+	  prevent network congestion. Review:
+	  https://reviewboard.asterisk.org/r/4246/ ASTERISK-24342 #close
+	  Reported by: Richard Mudgett ........ Merged revisions 429127
+	  from http://svn.asterisk.org/svn/asterisk/branches/12
 
 2014-12-15  Asterisk Development Team <asteriskteam at digium.com>
 
diff --git a/Makefile b/Makefile
index 66a8411..d4ae9fd 100644
--- a/Makefile
+++ b/Makefile
@@ -124,7 +124,7 @@ empty:=
 space:=$(empty) $(empty)
 ASTTOPDIR:=$(subst $(space),\$(space),$(CURDIR))
 
-# Overwite config files on "make samples"
+# Overwite config files on "make samples" or other config installation targets
 OVERWRITE=y
 
 # Include debug and macro symbols in the executables (-g) and profiling info (-pg)
@@ -154,14 +154,21 @@ LINKER_SYMBOL_PREFIX=
 # Default install directory for DAHDI hooks.
 DAHDI_UDEV_HOOK_DIR = /usr/share/dahdi/span_config.d
 
-# If the file .asterisk.makeopts is present in your home directory, you can
-# include all of your favorite menuselect options so that every time you download
-# a new version of Asterisk, you don't have to run menuselect to set them.
-# The file /etc/asterisk.makeopts will also be included but can be overridden
-# by the file in your home directory.
+# This Makefile previously contained a note about the ability to use .asterisk.makeopts
+# from your home directory or /etc/asterisk.makeopts to set defaults for menuselect.
+# These files have never worked in this branch of Asterisk.  The work around is to
+# manually copy the file containing defaults before running 'make menuselect':
+#
+# cp ${HOME}/.asterisk.makeopts menuselect.makeopts
+#   or
+# cp /etc/asterisk.makeopts menuselect.makeopts
+#
+# As an alternative, menuselect/menuselect can be used by a script to enable or disable
+# individual options or entire categories.  To use this feature you must first
+# compile menuselect using 'make menuselect.makeopts'.  For information about parameters
+# supported run:
+# menuselect/menuselect --help
 
-GLOBAL_MAKEOPTS=$(wildcard /etc/asterisk.makeopts)
-USER_MAKEOPTS=$(wildcard ~/.asterisk.makeopts)
 
 MOD_SUBDIR_CFLAGS="-I$(ASTTOPDIR)/include"
 OTHER_SUBDIR_CFLAGS="-I$(ASTTOPDIR)/include"
@@ -184,7 +191,7 @@ ifeq ($(findstring -Wall,$(_ASTCFLAGS) $(ASTCFLAGS)),)
   _ASTCFLAGS+=-Wall
 endif
 
-_ASTCFLAGS+=-Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations $(AST_NESTED_FUNCTIONS) $(DEBUG)
+_ASTCFLAGS+=-Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations $(AST_NESTED_FUNCTIONS) $(AST_CLANG_BLOCKS) $(DEBUG)
 ADDL_TARGETS=
 
 ifeq ($(AST_DEVMODE),yes)
@@ -254,10 +261,10 @@ MOD_SUBDIRS_MENUSELECT_TREE:=$(MOD_SUBDIRS:%=%-menuselect-tree)
 
 ifneq ($(findstring darwin,$(OSARCH)),)
   _ASTCFLAGS+=-D__Darwin__ -mmacosx-version-min=10.6
-  _SOLINK=-mmacosx-version-min=10.6 -Xlinker -undefined -Xlinker dynamic_lookup
+  _SOLINK=-mmacosx-version-min=10.6 -Wl,-undefined,dynamic_lookup
   _SOLINK+=/usr/lib/bundle1.o
   SOLINK=-bundle $(_SOLINK)
-  DYLINK=-Xlinker -dylib $(_SOLINK)
+  DYLINK=-Wl,-dylib $(_SOLINK)
   _ASTLDFLAGS+=-L/usr/local/lib
 else
 # These are used for all but Darwin
@@ -332,10 +339,10 @@ makeopts: configure
 	@echo "****"
 	@exit 1
 
-menuselect.makeopts: menuselect/menuselect menuselect-tree makeopts build_tools/menuselect-deps $(GLOBAL_MAKEOPTS) $(USER_MAKEOPTS)
+menuselect.makeopts: menuselect/menuselect menuselect-tree makeopts build_tools/menuselect-deps
 ifeq ($(filter %menuselect,$(MAKECMDGOALS)),)
 	menuselect/menuselect --check-deps $@
-	menuselect/menuselect --check-deps $@ $(GLOBAL_MAKEOPTS) $(USER_MAKEOPTS)
+	menuselect/menuselect --check-deps $@
 endif
 
 $(MOD_SUBDIRS_EMBED_LDSCRIPT):
@@ -393,7 +400,7 @@ defaults.h: makeopts .lastclean build_tools/make_defaults_h
 	@cmp -s $@.tmp $@ || mv $@.tmp $@
 	@rm -f $@.tmp
 
-main/version.c: FORCE .lastclean
+main/version.c: FORCE menuselect.makeopts .lastclean
 	@build_tools/make_version_c > $@.tmp
 	@cmp -s $@.tmp $@ || mv $@.tmp $@
 	@rm -f $@.tmp
@@ -423,6 +430,8 @@ _clean:
 	rm -f doc/core-en_US.xml
 	rm -f doc/full-en_US.xml
 	rm -f doc/rest-api/*.wiki
+	rm -f doxygen.log
+	rm -rf latex
 	rm -f rest-api-templates/*.pyc
 	@$(MAKE) -C menuselect clean
 	cp -f .cleancount .lastclean
@@ -439,6 +448,7 @@ distclean: $(SUBDIRS_DIST_CLEAN) _clean
 	rm -f include/asterisk/autoconfig.h
 	rm -f include/asterisk/buildopts.h
 	rm -rf doc/api
+	rm -f doc/asterisk-ng-doxygen
 	rm -f build_tools/menuselect-deps
 
 datafiles: _all doc/core-en_US.xml
@@ -569,6 +579,7 @@ main-bininstall:
 	+ at DESTDIR="$(DESTDIR)" ASTSBINDIR="$(ASTSBINDIR)" ASTLIBDIR="$(ASTLIBDIR)" $(SUBMAKE) -C main bininstall
 
 bininstall: _all installdirs $(SUBDIRS_INSTALL) main-bininstall
+	$(INSTALL) -m 755 contrib/scripts/astversion "$(DESTDIR)$(ASTSBINDIR)/"
 	$(INSTALL) -m 755 contrib/scripts/astgenkey "$(DESTDIR)$(ASTSBINDIR)/"
 	$(INSTALL) -m 755 contrib/scripts/autosupport "$(DESTDIR)$(ASTSBINDIR)/"
 	if [ ! -f /sbin/launchd ]; then \
@@ -641,7 +652,12 @@ install: badshell bininstall datafiles
 	@echo " + configuration files (overwriting any      +"
 	@echo " + existing config files), run:              +"
 	@echo " +                                           +"
-	@echo " +               $(mK) samples               +"
+	@echo " + For generic reference documentation:      +"
+	@echo " +   $(mK) samples                           +"
+	@echo " +                                           +"
+	@echo " + For a sample basic PBX:                   +"
+	@echo " +   $(mK) basic-pbx                         +"
+	@echo " +                                           +"
 	@echo " +                                           +"
 	@echo " +-----------------  or ---------------------+"
 	@echo " +                                           +"
@@ -659,24 +675,14 @@ isntall: install
 
 upgrade: bininstall
 
-# XXX why *.adsi is installed first ?
-adsi:
-	@echo Installing adsi config files...
-	$(INSTALL) -d "$(DESTDIR)$(ASTETCDIR)"
-	@for x in configs/samples/*.adsi; do \
-		dst="$(DESTDIR)$(ASTETCDIR)/`$(BASENAME) $$x`" ; \
-		if [ -f "$${dst}" ] ; then \
-			echo "Overwriting $$x" ; \
-		else \
-			echo "Installing $$x" ; \
-		fi ; \
-		$(INSTALL) -m 644 "$$x" "$(DESTDIR)$(ASTETCDIR)/`$(BASENAME) $$x`" ; \
-	done
 
-samples: adsi
-	@echo Installing other config files...
-	@for x in configs/samples/*.sample; do \
-		dst="$(DESTDIR)$(ASTETCDIR)/`$(BASENAME) $$x .sample`" ;	\
+# Install configuration files from the specified directory
+# Parameters:
+#  (1) the configuration directory to install from
+#  (2) the extension to strip off
+define INSTALL_CONFIGS
+	@for x in configs/$(1)/*$(2); do \
+		dst="$(DESTDIR)$(ASTETCDIR)/`$(BASENAME) $$x $(2)`"; \
 		if [ -f "$${dst}" ]; then \
 			if [ "$(OVERWRITE)" = "y" ]; then \
 				if cmp -s "$${dst}" "$$x" ; then \
@@ -691,7 +697,7 @@ samples: adsi
 		fi ; \
 		echo "Installing file $$x"; \
 		$(INSTALL) -m 644 "$$x" "$${dst}" ;\
-	done
+	done ; \
 	if [ "$(OVERWRITE)" = "y" ]; then \
 		echo "Updating asterisk.conf" ; \
 		sed -e 's|^astetcdir.*$$|astetcdir => $(ASTETCDIR)|' \
@@ -708,10 +714,28 @@ samples: adsi
 			"$(DESTDIR)$(ASTCONFPATH)" > "$(DESTDIR)$(ASTCONFPATH).tmp" ; \
 		$(INSTALL) -m 644 "$(DESTDIR)$(ASTCONFPATH).tmp" "$(DESTDIR)$(ASTCONFPATH)" ; \
 		rm -f "$(DESTDIR)$(ASTCONFPATH).tmp" ; \
-	fi ; \
+	fi
+endef
+
+# XXX why *.adsi is installed first ?
+adsi:
+	@echo Installing adsi config files...
+	$(INSTALL) -d "$(DESTDIR)$(ASTETCDIR)"
+	@for x in configs/samples/*.adsi; do \
+		dst="$(DESTDIR)$(ASTETCDIR)/`$(BASENAME) $$x`" ; \
+		if [ -f "$${dst}" ] ; then \
+			echo "Overwriting $$x" ; \
+		else \
+			echo "Installing $$x" ; \
+		fi ; \
+		$(INSTALL) -m 644 "$$x" "$(DESTDIR)$(ASTETCDIR)/`$(BASENAME) $$x`" ; \
+	done
+
+samples: adsi
+	@echo Installing other config files...
+	$(call INSTALL_CONFIGS,samples,.sample)
 	$(INSTALL) -d "$(DESTDIR)$(ASTSPOOLDIR)/voicemail/default/1234/INBOX"
 	build_tools/make_sample_voicemail "$(DESTDIR)/$(ASTDATADIR)" "$(DESTDIR)/$(ASTSPOOLDIR)"
-
 	@for x in phoneprov/*; do \
 		dst="$(DESTDIR)$(ASTDATADIR)/$$x" ;	\
 		if [ -f "$${dst}" ]; then \
@@ -730,6 +754,10 @@ samples: adsi
 		$(INSTALL) -m 644 "$$x" "$${dst}" ;\
 	done
 
+basic-pbx:
+	@echo Installing basic-pbx config files...
+	$(call INSTALL_CONFIGS,basic-pbx)
+
 webvmail:
 	@[ -d "$(DESTDIR)$(HTTP_DOCSDIR)/" ] || ( printf "http docs directory not found.\nUpdate assignment of variable HTTP_DOCSDIR in Makefile!\n" && exit 1 )
 	@[ -d "$(DESTDIR)$(HTTP_CGIDIR)" ] || ( printf "cgi-bin directory not found.\nUpdate assignment of variable HTTP_CGIDIR in Makefile!\n" && exit 1 )
@@ -757,7 +785,8 @@ webvmail:
 	@echo " +-------------------------------------------+"
 
 progdocs:
-	# Note, Makefile conditionals must not be tabbed out. Wasted hours with that.
+# Note, Makefile conditionals must not be tabbed out. Wasted hours with that.
+	@cp doc/asterisk-ng-doxygen.in doc/asterisk-ng-doxygen
 ifeq ($(DOXYGEN),:)
 	@echo "Doxygen is not installed.  Please install and re-run the configuration script."
 else
@@ -765,20 +794,20 @@ ifeq ($(DOT),:)
 	@echo "DOT is not installed. Doxygen will not produce any diagrams. Please install and re-run the configuration script."
 else
 	# Enable DOT
-	@echo "HAVE_DOT = YES" >> contrib/asterisk-ng-doxygen
+	@echo "HAVE_DOT = YES" >> doc/asterisk-ng-doxygen
 endif
 	# Set Doxygen PROJECT_NUMBER variable
 ifneq ($(ASTERISKVERSION),UNKNOWN__and_probably_unsupported)
-	@echo "PROJECT_NUMBER = $(ASTERISKVERSION)" >> contrib/asterisk-ng-doxygen
+	@echo "PROJECT_NUMBER = $(ASTERISKVERSION)" >> doc/asterisk-ng-doxygen
 else
 	echo "Asterisk Version is unknown, not configuring Doxygen PROJECT_NUMBER."
 endif
-	# Validate Doxygen Configuration
-	@doxygen -u contrib/asterisk-ng-doxygen
+	# Validate and auto-update local copy
+	@doxygen -u doc/asterisk-ng-doxygen
 	# Run Doxygen
-	@doxygen contrib/asterisk-ng-doxygen
+	@doxygen doc/asterisk-ng-doxygen
 	# Remove configuration backup file
-	@rm -f contrib/asterisk-ng-doxygen.bak
+	@rm -f doc/asterisk-ng-doxygen.bak
 endif
 
 install-logrotate:
@@ -931,7 +960,7 @@ nmenuselect: menuselect/nmenuselect menuselect-tree menuselect.makeopts
 	- at menuselect/nmenuselect menuselect.makeopts && (echo "menuselect changes saved!"; rm -f channels/h323/Makefile.ast main/asterisk) || echo "menuselect changes NOT saved!"
 
 # options for make in menuselect/
-MAKE_MENUSELECT=CC="$(BUILD_CC)" CXX="" LD="" AR="" RANLIB="" \
+MAKE_MENUSELECT=CC="$(BUILD_CC)" CXX="$(CXX)" LD="" AR="" RANLIB="" \
 		CFLAGS="$(BUILD_CFLAGS)" LDFLAGS="$(BUILD_LDFLAGS)" \
 		$(MAKE) -C menuselect CONFIGURE_SILENT="--silent"
 
@@ -976,6 +1005,7 @@ ifeq ($(PYTHON),:)
 	@echo "--------------------------------------------------------------------------"
 	@false
 else
+	@$(INSTALL) -d doc/rest-api
 	$(PYTHON) rest-api-templates/make_ari_stubs.py \
 		rest-api/resources.json .
 endif
@@ -1000,6 +1030,7 @@ endif
 .PHONY: validate-docs
 .PHONY: _clean
 .PHONY: ari-stubs
+.PHONY: basic-pbx
 .PHONY: $(SUBDIRS_INSTALL)
 .PHONY: $(SUBDIRS_DIST_CLEAN)
 .PHONY: $(SUBDIRS_CLEAN)
diff --git a/Makefile.moddir_rules b/Makefile.moddir_rules
index 939e79f..7f1c8c8 100644
--- a/Makefile.moddir_rules
+++ b/Makefile.moddir_rules
@@ -118,6 +118,7 @@ clean::
 	rm -f *.so *.o *.oo *.eo *.i *.ii
 	rm -f .*.d
 	rm -f *.s *.i
+	rm -f *.gcda *.gcno
 	rm -f modules.link
 
 install:: all
@@ -133,12 +134,12 @@ dist-clean::
 
 .%.moduleinfo: %.c
 	@echo "<member name=\"$*\" displayname=\"$(shell $(GREP) -e AST_MODULE_INFO $< | head -n 1 | cut -d '"' -f 2)\" remove_on_change=\"$(SUBDIR)/$*.o $(SUBDIR)/$*.so\">" > $@
-	$(AWK) -f $(ASTTOPDIR)/build_tools/get_moduleinfo $< >> $@
+	$(AWK) -f $(ASTTOPDIR)/build_tools/get_moduleinfo $^ >> $@
 	echo "</member>" >> $@
 
 .%.moduleinfo: %.cc
 	@echo "<member name=\"$*\" displayname=\"$(shell $(GREP) -e AST_MODULE_INFO $< | head -n 1 | cut -d '"' -f 2)\" remove_on_change=\"$(SUBDIR)/$*.oo $(SUBDIR)/$*.so\">" > $@
-	$(AWK) -f $(ASTTOPDIR)/build_tools/get_moduleinfo $< >> $@
+	$(AWK) -f $(ASTTOPDIR)/build_tools/get_moduleinfo $^ >> $@
 	echo "</member>" >> $@
 
 .moduleinfo:: $(addsuffix .moduleinfo,$(addprefix .,$(sort $(ALL_C_MODS) $(ALL_CC_MODS))))
diff --git a/Makefile.rules b/Makefile.rules
index 41205bc..1031f2d 100644
--- a/Makefile.rules
+++ b/Makefile.rules
@@ -27,6 +27,26 @@
 # extra cflags to build dependencies. Recursively expanded.
 MAKE_DEPS=-MD -MT $@ -MF .$(subst /,_,$@).d -MP
 
+ifeq ($(findstring ADDRESS_SANITIZER,$(MENUSELECT_CFLAGS)),ADDRESS_SANITIZER)
+    _ASTLDFLAGS+=-fsanitize=address
+    _ASTCFLAGS+=-fno-omit-frame-pointer -fsanitize=address
+endif
+
+ifeq ($(findstring THREAD_SANITIZER,$(MENUSELECT_CFLAGS)),THREAD_SANITIZER)
+    _ASTLDFLAGS+=-fsanitize=thread -pie -fPIE
+    _ASTCFLAGS+=-fno-omit-frame-pointer -pie -fPIE -fsanitize=thread
+endif
+
+ifeq ($(findstring LEAK_SANITIZER,$(MENUSELECT_CFLAGS)),LEAK_SANITIZER)
+    _ASTLDFLAGS+=-fsanitize=leak
+    _ASTCFLAGS+=-fno-omit-frame-pointer -fsanitize=leak
+endif
+
+ifeq ($(findstring UNDEFINED_SANITIZER,$(MENUSELECT_CFLAGS)),UNDEFINED_SANITIZER)
+    _ASTLDFLAGS+=-fsanitize=undefined
+    _ASTCFLAGS+=-fno-omit-frame-pointer -fsanitize=undefined
+endif
+
 ifeq ($(NOISY_BUILD),)
     ECHO_PREFIX=@
     CMD_PREFIX=@
@@ -65,6 +85,11 @@ endif
 CC_CFLAGS=$(PTHREAD_CFLAGS) $(_ASTCFLAGS) $(ASTCFLAGS)
 CXX_CFLAGS=$(PTHREAD_CFLAGS) $(filter-out -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations $(AST_DECLARATION_AFTER_STATEMENT),$(_ASTCFLAGS) $(ASTCFLAGS))
 
+# Clang -Werror warning suppressions
+ifeq ($(C_COMPILER_FAMILY),clang)
+	CC_CFLAGS+=-Wno-unused-value -Wno-parentheses-equality
+endif
+
 ifeq ($(GNU_LD),1)
 SO_SUPPRESS_SYMBOLS=-Wl,--version-script,$(subst .so,.exports,$@),--warn-common
 ifneq ($(wildcard $(subst .so,.dynamics,$@)),)
@@ -101,10 +126,17 @@ endif
 
 %.o: %.i
 	$(ECHO_PREFIX) echo "   [CCi] $< -> $@"
+ifneq ($(AST_CLANG_BLOCKS),)
+ifeq ($(COMPILE_DOUBLE),yes)
+	$(CMD_PREFIX) $(CC) -o /dev/null -c $< $(CC_CFLAGS) $(OPTIMIZE) -Wno-unused-command-line-argument
+endif
+	$(CMD_PREFIX) $(CC) -o $@ -c $< $(CC_CFLAGS) $(_ASTCFLAGS_COVERAGE) -Wno-unused-command-line-argument
+else
 ifeq ($(COMPILE_DOUBLE),yes)
 	$(CMD_PREFIX) $(CC) -o /dev/null -c $< $(CC_CFLAGS) $(OPTIMIZE)
 endif
 	$(CMD_PREFIX) $(CC) -o $@ -c $< $(CC_CFLAGS) $(_ASTCFLAGS_COVERAGE)
+endif
 
 ifneq ($(COMPILE_DOUBLE),yes)
 %.o: %.c
diff --git a/UPGRADE.txt b/UPGRADE.txt
index 0c500da..27ca0e6 100644
--- a/UPGRADE.txt
+++ b/UPGRADE.txt
@@ -21,6 +21,102 @@
 === UPGRADE-12.txt  -- Upgrade info for 11 to 12
 ===========================================================
 
+From 13.5.0 to 13.6.0:
+
+ARI:
+ - The version of ARI has been updated to 1.9.0 to reflect the backwards
+   compatible changes outlined in the CHANGES file.
+
+From 13.4.0 to 13.5.0:
+
+AMI:
+ - The version of AMI has been bumped to 2.8.0 to account for backwards
+   compatible features included with this release.  See CHANGES for more
+   information.
+
+ARI:
+ - The version of ARI has been updated to 1.8.0 to reflect the backwards
+   compatible changes outlined in the CHANGES file.
+
+From 13.3.0 to 13.4.0:
+
+Source Control:
+ - Asterisk has moved from Subversion to Git. As a result, several changes
+   were required in functionality. These are listed individually in the
+   notes below.
+
+AMI:
+ - The 'ModuleCheck' Action's Version key will now always report the
+   current version of Asterisk.
+
+ARI:
+ - The version of ARI has been updated to 1.7.0 to reflect the backwards
+   compatible changes outlined in the CHANGES file.
+
+CLI:
+ - The 'core show file version' command has been altered. In the past,
+   this command would show the SVN revision of the source files compiled
+   in Asterisk. However, when Asterisk moved to Git, the source control
+   version support was removed. As a result, the version information shown
+   by the CLI command is always the Asterisk version. This CLI command
+   will be removed in Asterisk 14.
+
+chan_dahdi:
+ - Added the force_restart_unavailable_chans compatibility option.  When
+   enabled it causes Asterisk to restart the ISDN B channel if an outgoing
+   call receives cause 44 (Requested channel not available).  The new option
+   is enabled by default in current release branches for backward
+   compatibility.
+
+res_pjsip:
+ - The dtmf_mode now supports a new option, 'auto'. This mode will attempt to
+   detect if the device supports RFC4733 DTMF. If so, it will choose that
+   DTMF type; if not, it will choose 'inband' DTMF.
+
+res_pjsip_dlg_options:
+ - A new module, this handles OPTIONS requests sent in-dialog. This module
+   should have no adverse effects for those upgrading; this note merely
+   serves as an indication that a new module exists.
+
+cdr_odbc:
+ - Added support for post-1.8 CDR columns 'peeraccount', 'linkedid', and
+   'sequence'. Support for the new columns can be enabled via the newcdrcolumns
+   option in cdr_odbc.conf.
+
+cdr_csv:
+ - Added a new configuration option, "newcdrcolumns", which enables use of the
+   post-1.8 CDR columns 'peeraccount', 'linkedid', and 'sequence'.
+
+From 13.2.0 to 13.3.0:
+
+chan_dahdi:
+ - For users using the FXO port (FXS signaling) distinctive ring detection
+   feature, you will need to adjust the dringX count values.  The count
+   values now only record ring end events instead of any DAHDI event.  A
+   ring-ring-ring pattern would exceed the pattern limits and stop
+   Caller-ID detection.
+
+From 13.1.0 to 13.2.0:
+
+ARI:
+ - The version of ARI has been bumped to 1.7.0 to account for backwards
+   compatible features included with this release. See CHANGES for more
+   information.
+
+AMI:
+ - The version of AMI has been bumped to 2.7.0 to account for backwards
+   compatible features included with this release. See CHANGES for more
+   information.
+
+chan_dahdi:
+ - The CALLERID(ani2) value for incoming calls is now populated in featdmf
+   signaling mode.  The information was previously discarded.
+
+chan_iax2:
+ - The iax.conf forcejitterbuffer option has been removed.  It is now always
+   forced if you set iax.conf jitterbuffer=yes.  If you put a jitter buffer
+   on a channel it will be on the channel.
+
 From 13.0.0 to 13.1.0:
 
 ARI:
diff --git a/addons/cdr_mysql.c b/addons/cdr_mysql.c
index e4ba8bf..4fb3ce5 100644
--- a/addons/cdr_mysql.c
+++ b/addons/cdr_mysql.c
@@ -42,7 +42,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <mysql/mysql.h>
 #include <mysql/errmsg.h>
diff --git a/addons/chan_mobile.c b/addons/chan_mobile.c
index 020cd21..f748f79 100644
--- a/addons/chan_mobile.c
+++ b/addons/chan_mobile.c
@@ -42,7 +42,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 427611 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <pthread.h>
 #include <signal.h>
@@ -902,7 +902,7 @@ static struct ast_channel *mbl_request(const char *type, struct ast_format_cap *
 	}
 
 	if (ast_format_cap_iscompatible_format(cap, DEVICE_FRAME_FORMAT) == AST_FORMAT_CMP_NOT_EQUAL) {
-		struct ast_str *codec_buf = ast_str_alloca(64);
+		struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 		ast_log(LOG_WARNING, "Asked to get a channel of unsupported format '%s'\n", ast_format_cap_get_names(cap, &codec_buf));
 		*cause = AST_CAUSE_FACILITY_NOT_IMPLEMENTED;
 		return NULL;
diff --git a/addons/chan_ooh323.c b/addons/chan_ooh323.c
index 6268ad0..b530697 100644
--- a/addons/chan_ooh323.c
+++ b/addons/chan_ooh323.c
@@ -576,7 +576,7 @@ static struct ast_channel *ooh323_request(const char *type, struct ast_format_ca
 		const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *data, int *cause)
 
 {
-	struct ast_str *codec_buf = ast_str_alloca(64);
+	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 	struct ast_channel *chan = NULL;
 	struct ooh323_pvt *p = NULL;
 	struct ooh323_peer *peer = NULL;
@@ -1192,7 +1192,7 @@ static int ooh323_write(struct ast_channel *ast, struct ast_frame *f)
 
 			if (ast_format_cap_iscompatible_format(ast_channel_nativeformats(ast), f->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
 				if (ast_format_cap_count(ast_channel_nativeformats(ast))) {
-					struct ast_str *codec_buf = ast_str_alloca(64);
+					struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 					ast_log(LOG_WARNING,
 							"Asked to transmit frame type %s, while native formats is %s (read/write = %s/%s)\n",
 							ast_format_get_name(f->subclass.format),
@@ -1545,7 +1545,7 @@ void ooh323_set_write_format(ooCallData *call, struct ast_format *fmt, int txfra
 			return;
 		}
 		if (gH323Debug) {
-			struct ast_str *codec_buf = ast_str_alloca(64);
+			struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 			ast_verb(0, "Writeformat before update %s/%s\n", 
 			  ast_format_get_name(ast_channel_writeformat(p->owner)),
 			  ast_format_cap_get_names(ast_channel_nativeformats(p->owner), &codec_buf));
@@ -2114,7 +2114,7 @@ int onNewCallCreated(ooCallData *call)
 		}
 
 		if (gH323Debug) {
-			struct ast_str *codec_buf = ast_str_alloca(64);
+			struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 			ast_verb(0, " Outgoing call %s(%s) - Codec prefs - %s\n", 
 				p->username?p->username:"NULL", call->callToken,
@@ -3231,7 +3231,7 @@ static char *handle_cli_ooh323_show_peer(struct ast_cli_entry *e, int cmd, struc
 static char *handle_cli_ooh323_show_peers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
 	struct ooh323_peer *prev = NULL, *peer = NULL;
-	struct ast_str *codec_buf = ast_str_alloca(64);
+	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
    char ip_port[30];
 #define FORMAT  "%-15.15s  %-15.15s  %-23.23s  %-s\n"
 
@@ -3370,7 +3370,7 @@ static char *handle_cli_ooh323_show_user(struct ast_cli_entry *e, int cmd, struc
 static char *handle_cli_ooh323_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
 	struct ooh323_user *prev = NULL, *user = NULL;
-	struct ast_str *codec_buf = ast_str_alloca(64);
+	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 #define FORMAT1  "%-15.15s  %-15.15s  %-15.15s  %-s\n"
 
 	switch (cmd) {
@@ -3504,7 +3504,7 @@ static char *handle_cli_ooh323_show_gk(struct ast_cli_entry *e, int cmd, struct
 static char *handle_cli_ooh323_show_config(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
 	char value[FORMAT_STRING_SIZE];
-	struct ast_str *codec_buf = ast_str_alloca(64);
+	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 	ooAliases *pAlias = NULL, *pAliasNext = NULL;;
 
 	switch (cmd) {
@@ -4115,6 +4115,7 @@ int ooh323_destroy(struct ooh323_pvt *p)
 		}
 
 		if (cur->rtp) {
+			ast_rtp_instance_stop(cur->rtp);
 			ast_rtp_instance_destroy(cur->rtp);
 			cur->rtp = NULL;
 		}
diff --git a/addons/format_mp3.c b/addons/format_mp3.c
index 586f3ad..e354148 100644
--- a/addons/format_mp3.c
+++ b/addons/format_mp3.c
@@ -34,7 +34,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "mp3/mpg123.h"
 #include "mp3/mpglib.h"
diff --git a/addons/ooh323c/src/ooSocket.c b/addons/ooh323c/src/ooSocket.c
index e843f5e..ee02f52 100644
--- a/addons/ooh323c/src/ooSocket.c
+++ b/addons/ooh323c/src/ooSocket.c
@@ -15,7 +15,7 @@
  *****************************************************************************/
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 369602 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/io.h"
 #include "asterisk/lock.h"
diff --git a/addons/ooh323c/src/ooh245.c b/addons/ooh323c/src/ooh245.c
index 09dd3a4..39b3994 100644
--- a/addons/ooh323c/src/ooh245.c
+++ b/addons/ooh323c/src/ooh245.c
@@ -1909,7 +1909,7 @@ int ooOnReceivedOpenLogicalChannelAck(OOH323CallData *call,
                                      T_H245TransportAddress_unicastAddress)
    	{
       	 OOTRACEERR3("Error: Processing OpenLogicalChannelAck - media control "
-                  "channel addres type is not unicast (%s, %s)\n", 
+                  "channel address type is not unicast (%s, %s)\n", 
                    call->callType, call->callToken);
       	 return OO_FAILED;
    	}
diff --git a/addons/ooh323c/src/ooq931.c b/addons/ooh323c/src/ooq931.c
index 86ecd92..cbc4afb 100644
--- a/addons/ooh323c/src/ooq931.c
+++ b/addons/ooh323c/src/ooq931.c
@@ -758,14 +758,14 @@ int ooEncodeH225Message(OOH323CallData *call, Q931Message *pq931Msg,
       i += pq931Msg->causeIE->length;
    } 
       
-   /*Add progress indicator IE 
-   if(pq931Msg->messageType == Q931AlertingMsg || pq931Msg->messageType == Q931CallProceedingMsg)
+   /* Add progress indicator IE */
+   if(pq931Msg->messageType == Q931AlertingMsg || pq931Msg->messageType == Q931ProgressMsg)
    {
       msgbuf[i++] = Q931ProgressIndicatorIE;
       msgbuf[i++] = 2; //Length is 2 octet
       msgbuf[i++] = 0x80; //PI=8
       msgbuf[i++] = 0x88;
-  }*/
+   }
 
    /*Add display ie. for all but Status message as per ASTERISK-18748 */
    if(!ooUtilsIsStrEmpty(call->ourCallerId) && (pq931Msg->messageType != Q931StatusMsg))
diff --git a/addons/ooh323c/src/printHandler.c b/addons/ooh323c/src/printHandler.c
index 3986a86..8d51137 100644
--- a/addons/ooh323c/src/printHandler.c
+++ b/addons/ooh323c/src/printHandler.c
@@ -268,7 +268,7 @@ static const char* octStrToString
       if (bufsiz > 1) buffer[1] = '\0';
       for (i = 0; i < numocts; i++) {
          if (i < bufsiz - 1) {
-            sprintf (lbuf, "%02x", (unsigned)data[i]);
+            sprintf (lbuf, "%02hhx", (unsigned char)data[i]);
             strcat (&buffer[(i*2)+1], lbuf);
          }
          else break;
diff --git a/addons/res_config_mysql.c b/addons/res_config_mysql.c
index 1c70776..6e6224b 100644
--- a/addons/res_config_mysql.c
+++ b/addons/res_config_mysql.c
@@ -30,7 +30,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/stat.h>
 
@@ -1202,38 +1202,44 @@ static int require_mysql(const char *database, const char *tablename, va_list ap
 							PICK_WHICH_ALTER_ACTION(bigint)
 						}
 					}
-				} else if (strncmp(column->type, "float", 5) == 0 && !ast_rq_is_int(type) && type != RQ_FLOAT) {
-					if (table->database->requirements == RQ_WARN) {
-						ast_log(LOG_WARNING, "Realtime table %s@%s: Column %s cannot be a %s\n", tablename, database, column->name, column->type);
-						res = -1;
-					} else if (table->database->requirements == RQ_CREATECLOSE && modify_mysql(database, tablename, column, type, size) == 0) {
-						table_altered = 1;
-					} else if (table->database->requirements == RQ_CREATECHAR && modify_mysql(database, tablename, column, RQ_CHAR, size) == 0) {
-						table_altered = 1;
-					} else {
-						res = -1;
+				} else if (strncmp(column->type, "float", 5) == 0) {
+					if (!ast_rq_is_int(type) && type != RQ_FLOAT) {
+						if (table->database->requirements == RQ_WARN) {
+							ast_log(LOG_WARNING, "Realtime table %s@%s: Column %s cannot be a %s\n", tablename, database, column->name, column->type);
+							res = -1;
+						} else if (table->database->requirements == RQ_CREATECLOSE && modify_mysql(database, tablename, column, type, size) == 0) {
+							table_altered = 1;
+						} else if (table->database->requirements == RQ_CREATECHAR && modify_mysql(database, tablename, column, RQ_CHAR, size) == 0) {
+							table_altered = 1;
+						} else {
+							res = -1;
+						}
 					}
-				} else if ((strncmp(column->type, "datetime", 8) == 0 || strncmp(column->type, "timestamp", 9) == 0) && type != RQ_DATETIME) {
-					if (table->database->requirements == RQ_WARN) {
-						ast_log(LOG_WARNING, "Realtime table %s@%s: Column %s cannot be a %s\n", tablename, database, column->name, column->type);
-						res = -1;
-					} else if (table->database->requirements == RQ_CREATECLOSE && modify_mysql(database, tablename, column, type, size) == 0) {
-						table_altered = 1;
-					} else if (table->database->requirements == RQ_CREATECHAR && modify_mysql(database, tablename, column, RQ_CHAR, size) == 0) {
-						table_altered = 1;
-					} else {
-						res = -1;
+				} else if (strncmp(column->type, "datetime", 8) == 0 || strncmp(column->type, "timestamp", 9) == 0) {
+					if (type != RQ_DATETIME) {
+						if (table->database->requirements == RQ_WARN) {
+							ast_log(LOG_WARNING, "Realtime table %s@%s: Column %s cannot be a %s\n", tablename, database, column->name, column->type);
+							res = -1;
+						} else if (table->database->requirements == RQ_CREATECLOSE && modify_mysql(database, tablename, column, type, size) == 0) {
+							table_altered = 1;
+						} else if (table->database->requirements == RQ_CREATECHAR && modify_mysql(database, tablename, column, RQ_CHAR, size) == 0) {
+							table_altered = 1;
+						} else {
+							res = -1;
+						}
 					}
-				} else if ((strncmp(column->type, "date", 4) == 0) && type != RQ_DATE) {
-					if (table->database->requirements == RQ_WARN) {
-						ast_log(LOG_WARNING, "Realtime table %s@%s: Column %s cannot be a %s\n", tablename, database, column->name, column->type);
-						res = -1;
-					} else if (table->database->requirements == RQ_CREATECLOSE && modify_mysql(database, tablename, column, type, size) == 0) {
-						table_altered = 1;
-					} else if (table->database->requirements == RQ_CREATECHAR && modify_mysql(database, tablename, column, RQ_CHAR, size) == 0) {
-						table_altered = 1;
-					} else {
-						res = -1;
+				} else if (strncmp(column->type, "date", 4) == 0) {
+					if (type != RQ_DATE) {
+						if (table->database->requirements == RQ_WARN) {
+							ast_log(LOG_WARNING, "Realtime table %s@%s: Column %s cannot be a %s\n", tablename, database, column->name, column->type);
+							res = -1;
+						} else if (table->database->requirements == RQ_CREATECLOSE && modify_mysql(database, tablename, column, type, size) == 0) {
+							table_altered = 1;
+						} else if (table->database->requirements == RQ_CREATECHAR && modify_mysql(database, tablename, column, RQ_CHAR, size) == 0) {
+							table_altered = 1;
+						} else {
+							res = -1;
+						}
 					}
 				} else { /* Other, possibly unsupported types? */
 					if (table->database->requirements == RQ_WARN) {
diff --git a/apps/Makefile b/apps/Makefile
index 1d5d2b3..1e8be04 100644
--- a/apps/Makefile
+++ b/apps/Makefile
@@ -28,12 +28,12 @@ all: _all
 include $(ASTTOPDIR)/Makefile.moddir_rules
 
 clean::
-	rm -f confbridge/*.o confbridge/*.i
+	rm -f confbridge/*.o confbridge/*.i confbridge/*.gcda confbridge/*.gcno
 
 $(if $(filter app_confbridge,$(EMBEDDED_MODS)),modules.link,app_confbridge.so): $(subst .c,.o,$(wildcard confbridge/*.c))
 $(subst .c,.o,$(wildcard confbridge/*.c)): _ASTCFLAGS+=$(call MOD_ASTCFLAGS,app_confbridge)
 
 ifneq ($(findstring $(OSARCH), mingw32 cygwin ),)
-  LIBS+= -lres_features.so -lres_ael_share.so -lres_monitor.so -lres_speech.so
+  LIBS+= -lres_ael_share.so -lres_monitor.so -lres_speech.so
   LIBS+= -lres_smdi.so
 endif
diff --git a/apps/app_adsiprog.c b/apps/app_adsiprog.c
index 63c544d..efe1aa3 100644
--- a/apps/app_adsiprog.c
+++ b/apps/app_adsiprog.c
@@ -41,7 +41,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <netinet/in.h>
 #include <ctype.h>
@@ -1456,7 +1456,7 @@ static void dump_message(char *type, char *vname, unsigned char *buf, int buflen
 	int x;
 	printf("%s %s: [ ", type, vname);
 	for (x = 0; x < buflen; x++)
-		printf("%02x ", buf[x]);
+		printf("%02hhx ", buf[x]);
 	printf("]\n");
 }
 #endif
diff --git a/apps/app_agent_pool.c b/apps/app_agent_pool.c
index 0611b64..a8f2d2c 100644
--- a/apps/app_agent_pool.c
+++ b/apps/app_agent_pool.c
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 427512 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/cli.h"
 #include "asterisk/app.h"
@@ -552,14 +552,10 @@ static int load_config(void)
 	aco_option_register(&cfg_info, "fullname", ACO_EXACT, agent_types, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct agent_cfg, full_name));
 
 	if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
-		goto error;
+		return -1;
 	}
 
 	return 0;
-
-error:
-	destroy_config();
-	return -1;
 }
 
 enum agent_state {
@@ -730,12 +726,17 @@ static struct ast_channel *agent_lock_logged(struct agent_pvt *agent)
  */
 static enum ast_device_state agent_pvt_devstate_get(const char *agent_id)
 {
-	RAII_VAR(struct agent_pvt *, agent, ao2_find(agents, agent_id, OBJ_KEY), ao2_cleanup);
+	enum ast_device_state dev_state = AST_DEVICE_INVALID;
+	struct agent_pvt *agent;
 
+	agent = ao2_find(agents, agent_id, OBJ_KEY);
 	if (agent) {
-		return agent->devstate;
+		agent_lock(agent);
+		dev_state = agent->devstate;
+		agent_unlock(agent);
+		ao2_ref(agent, -1);
 	}
-	return AST_DEVICE_INVALID;
+	return dev_state;
 }
 
 /*!
@@ -2525,13 +2526,14 @@ static int action_agents(struct mansession *s, const struct message *m)
 	struct ao2_iterator iter;
 	struct agent_pvt *agent;
 	struct ast_str *out = ast_str_alloca(4096);
+	int num_agents = 0;
 
 	if (!ast_strlen_zero(id)) {
 		snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
 	} else {
 		id_text[0] = '\0';
 	}
-	astman_send_ack(s, m, "Agents will follow");
+	astman_send_listack(s, m, "Agents will follow", "start");
 
 	iter = ao2_iterator_init(agents, 0);
 	for (; (agent = ao2_iterator_next(&iter)); ao2_ref(agent, -1)) {
@@ -2586,12 +2588,12 @@ static int action_agents(struct mansession *s, const struct message *m)
 		astman_append(s, "Event: Agents\r\n"
 			"%s%s\r\n",
 			ast_str_buffer(out), id_text);
+		++num_agents;
 	}
 	ao2_iterator_destroy(&iter);
 
-	astman_append(s, "Event: AgentsComplete\r\n"
-		"%s"
-		"\r\n", id_text);
+	astman_send_list_complete_start(s, m, "AgentsComplete", num_agents);
+	astman_send_list_complete_end(s);
 	return 0;
 }
 
@@ -2641,7 +2643,7 @@ static int unload_module(void)
 	}
 
 	destroy_config();
-	ao2_ref(agents, -1);
+	ao2_cleanup(agents);
 	agents = NULL;
 	return 0;
 }
@@ -2655,12 +2657,6 @@ static int load_module(void)
 	if (!agents) {
 		return AST_MODULE_LOAD_FAILURE;
 	}
-	if (load_config()) {
-		ast_log(LOG_ERROR, "Unable to load config. Not loading module.\n");
-		ao2_ref(agents, -1);
-		agents = NULL;
-		return AST_MODULE_LOAD_DECLINE;
-	}
 
 	/* Init agent holding bridge v_table. */
 	bridge_init_agent_hold();
@@ -2686,6 +2682,13 @@ static int load_module(void)
 		unload_module();
 		return AST_MODULE_LOAD_FAILURE;
 	}
+
+	if (load_config()) {
+		ast_log(LOG_ERROR, "Unable to load config. Not loading module.\n");
+		unload_module();
+		return AST_MODULE_LOAD_DECLINE;
+	}
+
 	return AST_MODULE_LOAD_SUCCESS;
 }
 
diff --git a/apps/app_alarmreceiver.c b/apps/app_alarmreceiver.c
index b31e76d..aaf956f 100644
--- a/apps/app_alarmreceiver.c
+++ b/apps/app_alarmreceiver.c
@@ -45,7 +45,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <math.h>
 #include <sys/wait.h>
@@ -484,7 +484,7 @@ static int ademco_verify_checksum(char *event, int expected)
 }
 
 /*!
- * \brief Send a single tone burst for a specifed duration and frequency.
+ * \brief Send a single tone burst for a specified duration and frequency.
  * \since 11.0
  *
  * \param chan Asterisk Channel
diff --git a/apps/app_amd.c b/apps/app_amd.c
index b527aa9..68d1008 100644
--- a/apps/app_amd.c
+++ b/apps/app_amd.c
@@ -43,7 +43,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/lock.h"
@@ -126,6 +126,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
 						Voice Duration - Greeting.
 					</value>
 					<value name="MAXWORDLENGTH">
+						Word Length - max length of a single word.
+					</value>
+					<value name="MAXWORDS">
 						Word Count - maximum number of words.
 					</value>	
 				</variable>
diff --git a/apps/app_authenticate.c b/apps/app_authenticate.c
index af81d02..a837058 100644
--- a/apps/app_authenticate.c
+++ b/apps/app_authenticate.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 391947 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/lock.h"
 #include "asterisk/file.h"
diff --git a/apps/app_bridgewait.c b/apps/app_bridgewait.c
index d40f6c8..1e722f1 100644
--- a/apps/app_bridgewait.c
+++ b/apps/app_bridgewait.c
@@ -34,7 +34,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419539 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/file.h"
 #include "asterisk/channel.h"
diff --git a/apps/app_cdr.c b/apps/app_cdr.c
index 57b8402..bf4961a 100644
--- a/apps/app_cdr.c
+++ b/apps/app_cdr.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 405314 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/channel.h"
 #include "asterisk/module.h"
diff --git a/apps/app_celgenuserevent.c b/apps/app_celgenuserevent.c
index 69884fe..3037ef6 100644
--- a/apps/app_celgenuserevent.c
+++ b/apps/app_celgenuserevent.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/app.h"
diff --git a/apps/app_chanisavail.c b/apps/app_chanisavail.c
index 2a4aefd..84200db 100644
--- a/apps/app_chanisavail.c
+++ b/apps/app_chanisavail.c
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/ioctl.h>
 
@@ -56,10 +56,10 @@ static const char app[] = "ChanIsAvail";
 			<parameter name="Technology/Resource" required="true" argsep="&">
 				<argument name="Technology2/Resource2" multiple="true">
 					<para>Optional extra devices to check</para>
-					<para>If you need more then one enter them as
-					Technology2/Resource2&Technology3/Resourse3&.....</para>
+					<para>If you need more than one enter them as
+					Technology2/Resource2&Technology3/Resource3&.....</para>
 				</argument>
-				<para>Specification of the device(s) to check.  These must be in the format of 
+				<para>Specification of the device(s) to check.  These must be in the format of
 				<literal>Technology/Resource</literal>, where <replaceable>Technology</replaceable>
 				represents a particular channel driver, and <replaceable>Resource</replaceable>
 				represents a resource available to that particular channel driver.</para>
diff --git a/apps/app_channelredirect.c b/apps/app_channelredirect.c
index 0ebae5b..f636e02 100644
--- a/apps/app_channelredirect.c
+++ b/apps/app_channelredirect.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 389378 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/file.h"
 #include "asterisk/channel.h"
diff --git a/apps/app_chanspy.c b/apps/app_chanspy.c
index 972cc7b..9080594 100644
--- a/apps/app_chanspy.c
+++ b/apps/app_chanspy.c
@@ -35,7 +35,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 424507 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <ctype.h>
 #include <errno.h>
@@ -698,6 +698,9 @@ static int channel_spy(struct ast_channel *chan, struct ast_autochan *spyee_auto
 	while (ast_waitfor(chan, -1) > -1 && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) {
 		if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
 			running = -1;
+			if (f) {
+				ast_frfree(f);
+			}
 			break;
 		}
 
diff --git a/apps/app_confbridge.c b/apps/app_confbridge.c
index c13a830..5976d39 100644
--- a/apps/app_confbridge.c
+++ b/apps/app_confbridge.c
@@ -43,7 +43,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428339 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -118,6 +118,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428339 $")
 					<value name="KICKED">The channel was kicked from the conference.</value>
 					<value name="ENDMARKED">The channel left the conference as a result of the last marked user leaving.</value>
 					<value name="DTMF">The channel pressed a DTMF sequence to exit the conference.</value>
+					<value name="TIMEOUT">The channel reached its configured timeout.</value>
 				</variable>
 			</variablelist>
 		</description>
@@ -324,14 +325,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428339 $")
 
 static const char app[] = "ConfBridge";
 
-/* Number of buckets our conference bridges container can have */
+/*! Number of buckets our conference bridges container can have */
 #define CONFERENCE_BRIDGE_BUCKETS 53
 
-enum {
-	CONF_RECORD_EXIT = 0,
-	CONF_RECORD_START,
-	CONF_RECORD_STOP,
-};
+/*! Initial recording filename space. */
+#define RECORD_FILENAME_INITIAL_SPACE	128
 
 /*! \brief Container to hold all conference bridges in progress */
 struct ao2_container *conference_bridges;
@@ -557,29 +555,31 @@ static void send_unmute_event(struct confbridge_user *user, struct confbridge_co
 static void set_rec_filename(struct confbridge_conference *conference, struct ast_str **filename, int is_new)
 {
 	char *rec_file = conference->b_profile.rec_file;
-	time_t now;
 	char *ext;
+	time_t now;
 
-	if (ast_str_strlen(*filename) && ast_test_flag(&conference->b_profile, BRIDGE_OPT_RECORD_FILE_APPEND) && !is_new) {
-		    return;
+	if (ast_str_strlen(*filename)
+		&& ast_test_flag(&conference->b_profile, BRIDGE_OPT_RECORD_FILE_APPEND)
+		&& !is_new) {
+		return;
 	}
 
 	time(&now);
 
 	ast_str_reset(*filename);
 	if (ast_strlen_zero(rec_file)) {
-		ast_str_set(filename, 0, "confbridge-%s-%u.wav", conference->name, (unsigned int)now);
+		ast_str_set(filename, 0, "confbridge-%s-%u.wav", conference->name,
+			(unsigned int) now);
 	} else {
 		/* insert time before file extension */
 		ext = strrchr(rec_file, '.');
 		if (ext) {
 			ast_str_set_substr(filename, 0, rec_file, ext - rec_file);
-			ast_str_append(filename, 0, "-%u%s", (unsigned int)now, ext);
+			ast_str_append(filename, 0, "-%u%s", (unsigned int) now, ext);
 		} else {
-			ast_str_set(filename, 0, "%s-%u", rec_file, (unsigned int)now);
+			ast_str_set(filename, 0, "%s-%u", rec_file, (unsigned int) now);
 		}
 	}
-
 	if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_RECORD_FILE_APPEND)) {
 		ast_str_append(filename, 0, ",a");
 	}
@@ -589,10 +589,11 @@ static int is_new_rec_file(const char *rec_file, struct ast_str **orig_rec_file)
 {
 	if (!ast_strlen_zero(rec_file)) {
 		if (!*orig_rec_file) {
-			*orig_rec_file = ast_str_create(PATH_MAX);
+			*orig_rec_file = ast_str_create(RECORD_FILENAME_INITIAL_SPACE);
 		}
 
-		if (strcmp(ast_str_buffer(*orig_rec_file), rec_file)) {
+		if (*orig_rec_file
+			&& strcmp(ast_str_buffer(*orig_rec_file), rec_file)) {
 			ast_str_set(orig_rec_file, 0, "%s", rec_file);
 			return 1;
 		}
@@ -600,79 +601,48 @@ static int is_new_rec_file(const char *rec_file, struct ast_str **orig_rec_file)
 	return 0;
 }
 
-static void *record_thread(void *obj)
-{
-	struct confbridge_conference *conference = obj;
-	struct ast_app *mixmonapp = pbx_findapp("MixMonitor");
-	struct ast_channel *chan;
-	struct ast_str *filename = ast_str_alloca(PATH_MAX);
-	struct ast_str *orig_rec_file = NULL;
-	struct ast_bridge_features features;
-
-	ast_mutex_lock(&conference->record_lock);
-	if (!mixmonapp) {
-		ast_log(LOG_WARNING, "Can not record ConfBridge, MixMonitor app is not installed\n");
-		conference->record_thread = AST_PTHREADT_NULL;
-		ast_mutex_unlock(&conference->record_lock);
-		ao2_ref(conference, -1);
-		return NULL;
-	}
-	if (ast_bridge_features_init(&features)) {
-		ast_bridge_features_cleanup(&features);
-		conference->record_thread = AST_PTHREADT_NULL;
-		ast_mutex_unlock(&conference->record_lock);
-		ao2_ref(conference, -1);
-		return NULL;
-	}
-	ast_set_flag(&features.feature_flags, AST_BRIDGE_CHANNEL_FLAG_IMMOVABLE);
-
-	/* XXX If we get an EXIT right here, START will essentially be a no-op */
-	while (conference->record_state != CONF_RECORD_EXIT) {
-		set_rec_filename(conference, &filename,
-			is_new_rec_file(conference->b_profile.rec_file, &orig_rec_file));
-		chan = ast_channel_ref(conference->record_chan);
-		ast_answer(chan);
-		pbx_exec(chan, mixmonapp, ast_str_buffer(filename));
-		ast_bridge_join(conference->bridge, chan, NULL, &features, NULL, 0);
-
-		ast_hangup(chan); /* This will eat this thread's reference to the channel as well */
-		/* STOP has been called. Wait for either a START or an EXIT */
-		ast_cond_wait(&conference->record_cond, &conference->record_lock);
-	}
-	ast_bridge_features_cleanup(&features);
-	ast_free(orig_rec_file);
-	ast_mutex_unlock(&conference->record_lock);
-	ao2_ref(conference, -1);
-	return NULL;
-}
-
-/*! \brief Returns whether or not conference is being recorded.
+/*!
+ * \internal
+ * \brief Returns whether or not conference is being recorded.
+ *
  * \param conference The bridge to check for recording
+ *
+ * \note Must be called with the conference locked
+ *
  * \retval 1, conference is recording.
  * \retval 0, conference is NOT recording.
  */
 static int conf_is_recording(struct confbridge_conference *conference)
 {
-	return conference->record_state == CONF_RECORD_START;
+	return conference->record_chan != NULL;
 }
 
-/*! \brief Stop recording a conference bridge
+/*!
  * \internal
+ * \brief Stop recording a conference bridge
+ *
  * \param conference The conference bridge on which to stop the recording
+ *
+ * \note Must be called with the conference locked
+ *
  * \retval -1 Failure
  * \retval 0 Success
  */
 static int conf_stop_record(struct confbridge_conference *conference)
 {
 	struct ast_channel *chan;
-	if (conference->record_thread == AST_PTHREADT_NULL || !conf_is_recording(conference)) {
+	struct ast_frame f = { AST_FRAME_CONTROL, .subclass.integer = AST_CONTROL_HANGUP };
+
+	if (!conf_is_recording(conference)) {
 		return -1;
 	}
-	conference->record_state = CONF_RECORD_STOP;
-	chan = ast_channel_ref(conference->record_chan);
-	ast_bridge_remove(conference->bridge, chan);
-	ast_queue_frame(chan, &ast_null_frame);
-	chan = ast_channel_unref(chan);
+
+	/* Remove the recording channel from the conference bridge. */
+	chan = conference->record_chan;
+	conference->record_chan = NULL;
+	ast_queue_frame(chan, &f);
+	ast_channel_unref(chan);
+
 	ast_test_suite_event_notify("CONF_STOP_RECORD", "Message: stopped conference recording channel\r\nConference: %s", conference->b_profile.name);
 	send_stop_record_event(conference);
 
@@ -681,102 +651,119 @@ static int conf_stop_record(struct confbridge_conference *conference)
 
 /*!
  * \internal
- * \brief Stops the confbridge recording thread.
+ * \brief Start recording the conference
  *
- * \note Must be called with the conference locked
- */
-static int conf_stop_record_thread(struct confbridge_conference *conference)
-{
-	if (conference->record_thread == AST_PTHREADT_NULL) {
-		return -1;
-	}
-	conf_stop_record(conference);
-
-	ast_mutex_lock(&conference->record_lock);
-	conference->record_state = CONF_RECORD_EXIT;
-	ast_cond_signal(&conference->record_cond);
-	ast_mutex_unlock(&conference->record_lock);
-
-	pthread_join(conference->record_thread, NULL);
-	conference->record_thread = AST_PTHREADT_NULL;
-
-	/* this is the reference given to the channel during the channel alloc */
-	if (conference->record_chan) {
-		conference->record_chan = ast_channel_unref(conference->record_chan);
-	}
-
-	return 0;
-}
-
-/*! \brief Start recording the conference
- * \internal
- * \note The conference must be locked when calling this function
  * \param conference The conference bridge to start recording
+ *
+ * \note Must be called with the conference locked
+ *
  * \retval 0 success
- * \rteval non-zero failure
+ * \retval non-zero failure
  */
 static int conf_start_record(struct confbridge_conference *conference)
 {
+	struct ast_app *mixmonapp;
+	struct ast_channel *chan;
 	struct ast_format_cap *cap;
+	struct ast_bridge_features *features;
 
-	if (conference->record_state != CONF_RECORD_STOP) {
+	if (conf_is_recording(conference)) {
 		return -1;
 	}
 
-	if (!pbx_findapp("MixMonitor")) {
-		ast_log(LOG_WARNING, "Can not record ConfBridge, MixMonitor app is not installed\n");
+	mixmonapp = pbx_findapp("MixMonitor");
+	if (!mixmonapp) {
+		ast_log(LOG_WARNING, "Cannot record ConfBridge, MixMonitor app is not installed\n");
 		return -1;
 	}
 
+	features = ast_bridge_features_new();
+	if (!features) {
+		return -1;
+	}
+	ast_set_flag(&features->feature_flags, AST_BRIDGE_CHANNEL_FLAG_IMMOVABLE);
+
 	cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
 	if (!cap) {
+		ast_bridge_features_destroy(features);
 		return -1;
 	}
-
 	ast_format_cap_append(cap, ast_format_slin, 0);
 
-	conference->record_chan = ast_request("CBRec", cap, NULL, NULL,
-		conference->name, NULL);
+	/* Create the recording channel. */
+	chan = ast_request("CBRec", cap, NULL, NULL, conference->name, NULL);
 	ao2_ref(cap, -1);
-	if (!conference->record_chan) {
+	if (!chan) {
+		ast_bridge_features_destroy(features);
+		return -1;
+	}
+
+	/* Start recording. */
+	set_rec_filename(conference, &conference->record_filename,
+		is_new_rec_file(conference->b_profile.rec_file, &conference->orig_rec_file));
+	ast_answer(chan);
+	pbx_exec(chan, mixmonapp, ast_str_buffer(conference->record_filename));
+
+	/* Put the channel into the conference bridge. */
+	ast_channel_ref(chan);
+	conference->record_chan = chan;
+	if (ast_bridge_impart(conference->bridge, chan, NULL, features,
+		AST_BRIDGE_IMPART_CHAN_INDEPENDENT)) {
+		ast_hangup(chan);
+		ast_channel_unref(chan);
+		conference->record_chan = NULL;
 		return -1;
 	}
 
-	conference->record_state = CONF_RECORD_START;
-	ast_mutex_lock(&conference->record_lock);
-	ast_cond_signal(&conference->record_cond);
-	ast_mutex_unlock(&conference->record_lock);
 	ast_test_suite_event_notify("CONF_START_RECORD", "Message: started conference recording channel\r\nConference: %s", conference->b_profile.name);
 	send_start_record_event(conference);
 
 	return 0;
 }
 
-/*! \brief Start the recording thread on a conference bridge
- * \internal
- * \param conference The conference bridge on which to start the recording thread
- * \retval 0 success
- * \retval -1 failure
+/* \brief Playback the given filename and monitor for any dtmf interrupts.
+ *
+ * This function is used to playback sound files on a given channel and optionally
+ * allow dtmf interrupts to occur.
+ *
+ * If the optional bridge_channel parameter is given then sound file playback
+ * is played on that channel and dtmf interruptions are allowed. However, if
+ * bridge_channel is not set then the channel parameter is expected to be set
+ * instead and non interruptible playback is played on that channel.
+ *
+ * \param bridge_channel Bridge channel to play file on
+ * \param channel Optional channel to play file on if bridge_channel not given
+ * \param filename The file name to playback
+ *
+ * \retval -1 failure during playback, 0 on file was fully played, 1 on dtmf interrupt.
  */
-static int start_conf_record_thread(struct confbridge_conference *conference)
+static int play_file(struct ast_bridge_channel *bridge_channel, struct ast_channel *channel,
+		     const char *filename)
 {
-	conf_start_record(conference);
+	struct ast_channel *chan;
+	const char *stop_digits;
+	int digit;
 
-	/*
-	 * if the thread has already been started, don't start another
-	 */
-	if (conference->record_thread != AST_PTHREADT_NULL) {
-		return 0;
+	if (bridge_channel) {
+		chan = bridge_channel->chan;
+		stop_digits = AST_DIGIT_ANY;
+	} else {
+		chan = channel;
+		stop_digits = AST_DIGIT_NONE;
 	}
 
-	ao2_ref(conference, +1); /* give the record thread a ref */
-
-	if (ast_pthread_create_background(&conference->record_thread, NULL, record_thread, conference)) {
-		ast_log(LOG_WARNING, "Failed to create recording channel for conference %s\n", conference->name);
-		ao2_ref(conference, -1); /* error so remove ref */
+	digit = ast_stream_and_wait(chan, filename, stop_digits);
+	if (digit < 0) {
+		ast_log(LOG_WARNING, "Failed to playback file '%s' to channel\n", filename);
 		return -1;
 	}
 
+	if (digit > 0) {
+		ast_stopstream(bridge_channel->chan);
+		ast_bridge_channel_feature_digit_add(bridge_channel, digit);
+		return 1;
+	}
+
 	return 0;
 }
 
@@ -802,11 +789,13 @@ static int sound_file_exists(const char *filename)
  *
  * \param conference Conference bridge to peek at
  * \param user Optional Caller
+ * \param bridge_channel The bridged channel involved
  *
  * \note if caller is NULL, the announcment will be sent to all participants in the conference.
  * \return Returns 0 on success, -1 if the user hung up
  */
-static int announce_user_count(struct confbridge_conference *conference, struct confbridge_user *user)
+static int announce_user_count(struct confbridge_conference *conference, struct confbridge_user *user,
+			       struct ast_bridge_channel *bridge_channel)
 {
 	const char *other_in_party = conf_get_sound(CONF_SOUND_OTHER_IN_PARTY, conference->b_profile.sounds);
 	const char *only_one = conf_get_sound(CONF_SOUND_ONLY_ONE, conference->b_profile.sounds);
@@ -818,9 +807,7 @@ static int announce_user_count(struct confbridge_conference *conference, struct
 	} else if (conference->activeusers == 2) {
 		if (user) {
 			/* Eep, there is one other person */
-			if (ast_stream_and_wait(user->chan,
-				only_one,
-				"")) {
+			if (play_file(bridge_channel, user->chan, only_one) < 0) {
 				return -1;
 			}
 		} else {
@@ -837,9 +824,7 @@ static int announce_user_count(struct confbridge_conference *conference, struct
 			if (ast_say_number(user->chan, conference->activeusers - 1, "", ast_channel_language(user->chan), NULL)) {
 				return -1;
 			}
-			if (ast_stream_and_wait(user->chan,
-				other_in_party,
-				"")) {
+			if (play_file(bridge_channel, user->chan, other_in_party) < 0) {
 				return -1;
 			}
 		} else if (sound_file_exists(there_are) && sound_file_exists(other_in_party)) {
@@ -962,9 +947,11 @@ static void destroy_conference_bridge(void *obj)
 		conference->bridge = NULL;
 	}
 
+	ast_channel_cleanup(conference->record_chan);
+	ast_free(conference->orig_rec_file);
+	ast_free(conference->record_filename);
+
 	conf_bridge_profile_destroy(&conference->b_profile);
-	ast_cond_destroy(&conference->record_cond);
-	ast_mutex_destroy(&conference->record_lock);
 	ast_mutex_destroy(&conference->playback_lock);
 }
 
@@ -1207,7 +1194,9 @@ void conf_ended(struct confbridge_conference *conference)
 	/* Called with a reference to conference */
 	ao2_unlink(conference_bridges, conference);
 	send_conf_end_event(conference);
-	conf_stop_record_thread(conference);
+	ao2_lock(conference);
+	conf_stop_record(conference);
+	ao2_unlock(conference);
 }
 
 /*!
@@ -1258,12 +1247,15 @@ static struct confbridge_conference *join_conference_bridge(const char *conferen
 		/* Setup lock for playback channel */
 		ast_mutex_init(&conference->playback_lock);
 
-		/* Setup lock for the record channel */
-		ast_mutex_init(&conference->record_lock);
-		ast_cond_init(&conference->record_cond, NULL);
+		/* Setup for the record channel */
+		conference->record_filename = ast_str_create(RECORD_FILENAME_INITIAL_SPACE);
+		if (!conference->record_filename) {
+			ao2_ref(conference, -1);
+			ao2_unlock(conference_bridges);
+			return NULL;
+		}
 
 		/* Setup conference bridge parameters */
-		conference->record_thread = AST_PTHREADT_NULL;
 		ast_copy_string(conference->name, conference_name, sizeof(conference->name));
 		conf_bridge_profile_copy(&conference->b_profile, &user->b_profile);
 
@@ -1301,10 +1293,9 @@ static struct confbridge_conference *join_conference_bridge(const char *conferen
 		/* Set the initial state to EMPTY */
 		conference->state = CONF_STATE_EMPTY;
 
-		conference->record_state = CONF_RECORD_STOP;
 		if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_RECORD_CONFERENCE)) {
 			ao2_lock(conference);
-			start_conf_record_thread(conference);
+			conf_start_record(conference);
 			ao2_unlock(conference);
 		}
 
@@ -1352,7 +1343,7 @@ static struct confbridge_conference *join_conference_bridge(const char *conferen
 
 	/* Announce number of users if need be */
 	if (ast_test_flag(&user->u_profile, USER_OPT_ANNOUNCEUSERCOUNT)) {
-		if (announce_user_count(conference, user)) {
+		if (announce_user_count(conference, user, NULL)) {
 			leave_conference(user);
 			return NULL;
 		}
@@ -1367,7 +1358,7 @@ static struct confbridge_conference *join_conference_bridge(const char *conferen
 		 * joined the conference yet.
 		 */
 		ast_autoservice_start(user->chan);
-		user_count_res = announce_user_count(conference, NULL);
+		user_count_res = announce_user_count(conference, NULL, NULL);
 		ast_autoservice_stop(user->chan);
 		if (user_count_res) {
 			leave_conference(user);
@@ -1552,6 +1543,13 @@ static int conf_get_pin(struct ast_channel *chan, struct confbridge_user *user)
 	return -1;
 }
 
+static int user_timeout(struct ast_bridge_channel *bridge_channel, void *ignore)
+{
+	ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, 0);
+	pbx_builtin_setvar_helper(bridge_channel->chan, "CONFBRIDGE_RESULT", "TIMEOUT");
+	return -1;
+}
+
 static int conf_rec_name(struct confbridge_user *user, const char *conf_name)
 {
 	char destdir[PATH_MAX];
@@ -1786,6 +1784,16 @@ static int confbridge_exec(struct ast_channel *chan, const char *data)
 		ast_autoservice_stop(chan);
 	}
 
+	if (user.u_profile.timeout) {
+		ast_bridge_interval_hook(&user.features,
+			0,
+			user.u_profile.timeout * 1000,
+			user_timeout,
+			NULL,
+			NULL,
+			AST_BRIDGE_HOOK_REMOVE_ON_PULL);
+	}
+
 	/* See if we need to automatically set this user as a video source or not */
 	handle_video_on_join(conference, user.chan, ast_test_flag(&user.u_profile, USER_OPT_MARKEDUSER));
 
@@ -1808,6 +1816,12 @@ static int confbridge_exec(struct ast_channel *chan, const char *data)
 
 	/* if we're shutting down, don't attempt to do further processing */
 	if (ast_shutting_down()) {
+		/*
+		 * Not taking any new calls at this time.  We cannot create
+		 * the announcer channel if this is the first channel into
+		 * the conference and we certainly cannot create any
+		 * recording channel.
+		 */
 		leave_conference(&user);
 		conference = NULL;
 		goto confbridge_cleanup;
@@ -1863,7 +1877,8 @@ confbridge_cleanup:
 }
 
 static int action_toggle_mute(struct confbridge_conference *conference,
-	struct confbridge_user *user)
+			      struct confbridge_user *user,
+			      struct ast_bridge_channel *bridge_channel)
 {
 	int mute;
 
@@ -1886,10 +1901,9 @@ static int action_toggle_mute(struct confbridge_conference *conference,
 		send_unmute_event(user, conference);
 	}
 
-	return ast_stream_and_wait(user->chan, (mute ?
+	return play_file(bridge_channel, NULL, (mute ?
 		conf_get_sound(CONF_SOUND_MUTED, user->b_profile.sounds) :
-		conf_get_sound(CONF_SOUND_UNMUTED, user->b_profile.sounds)),
-		"");
+		conf_get_sound(CONF_SOUND_UNMUTED, user->b_profile.sounds))) < 0;
 }
 
 static int action_toggle_mute_participants(struct confbridge_conference *conference, struct confbridge_user *user)
@@ -2021,9 +2035,8 @@ static int action_kick_last(struct confbridge_conference *conference,
 	int isadmin = ast_test_flag(&user->u_profile, USER_OPT_ADMIN);
 
 	if (!isadmin) {
-		ast_stream_and_wait(bridge_channel->chan,
-			conf_get_sound(CONF_SOUND_ERROR_MENU, user->b_profile.sounds),
-			"");
+		play_file(bridge_channel, NULL,
+			  conf_get_sound(CONF_SOUND_ERROR_MENU, user->b_profile.sounds));
 		ast_log(LOG_WARNING, "Only admin users can use the kick_last menu action. Channel %s of conf %s is not an admin.\n",
 			ast_channel_name(bridge_channel->chan),
 			conference->name);
@@ -2034,9 +2047,8 @@ static int action_kick_last(struct confbridge_conference *conference,
 	if (((last_user = AST_LIST_LAST(&conference->active_list)) == user)
 		|| (ast_test_flag(&last_user->u_profile, USER_OPT_ADMIN))) {
 		ao2_unlock(conference);
-		ast_stream_and_wait(bridge_channel->chan,
-			conf_get_sound(CONF_SOUND_ERROR_MENU, user->b_profile.sounds),
-			"");
+		play_file(bridge_channel, NULL,
+			  conf_get_sound(CONF_SOUND_ERROR_MENU, user->b_profile.sounds));
 	} else if (last_user && !last_user->kicked) {
 		last_user->kicked = 1;
 		pbx_builtin_setvar_helper(last_user->chan, "CONFBRIDGE_RESULT", "KICKED");
@@ -2104,7 +2116,7 @@ static int execute_menu_entry(struct confbridge_conference *conference,
 	AST_LIST_TRAVERSE(&menu_entry->actions, menu_action, action) {
 		switch (menu_action->id) {
 		case MENU_ACTION_TOGGLE_MUTE:
-			res |= action_toggle_mute(conference, user);
+			res |= action_toggle_mute(conference, user, bridge_channel);
 			break;
 		case MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS:
 			if (!isadmin) {
@@ -2113,7 +2125,7 @@ static int execute_menu_entry(struct confbridge_conference *conference,
 			action_toggle_mute_participants(conference, user);
 			break;
 		case MENU_ACTION_PARTICIPANT_COUNT:
-			announce_user_count(conference, user);
+			announce_user_count(conference, user, bridge_channel);
 			break;
 		case MENU_ACTION_PLAYBACK:
 			if (!stop_prompts) {
@@ -2164,12 +2176,10 @@ static int execute_menu_entry(struct confbridge_conference *conference,
 				break;
 			}
 			conference->locked = (!conference->locked ? 1 : 0);
-			res |= ast_stream_and_wait(bridge_channel->chan,
+			res |= play_file(bridge_channel, NULL,
 				(conference->locked ?
 				conf_get_sound(CONF_SOUND_LOCKED_NOW, user->b_profile.sounds) :
-				conf_get_sound(CONF_SOUND_UNLOCKED_NOW, user->b_profile.sounds)),
-				"");
-
+				conf_get_sound(CONF_SOUND_UNLOCKED_NOW, user->b_profile.sounds))) < 0;
 			break;
 		case MENU_ACTION_ADMIN_KICK_LAST:
 			res |= action_kick_last(conference, bridge_channel, user);
@@ -2753,7 +2763,7 @@ static char *handle_cli_confbridge_start_record(struct ast_cli_entry *e, int cmd
 		ast_copy_string(conference->b_profile.rec_file, rec_file, sizeof(conference->b_profile.rec_file));
 	}
 
-	if (start_conf_record_thread(conference)) {
+	if (conf_start_record(conference)) {
 		ast_cli(a->fd, "Could not start recording due to internal error.\n");
 		ao2_unlock(conference);
 		ao2_ref(conference, -1);
@@ -2894,12 +2904,8 @@ static int action_confbridgelist(struct mansession *s, const struct message *m)
 	ao2_unlock(conference);
 	ao2_ref(conference, -1);
 
-	astman_append(s,
-	"Event: ConfbridgeListComplete\r\n"
-	"EventList: Complete\r\n"
-	"ListItems: %d\r\n"
-	"%s"
-	"\r\n", total, id_text);
+	astman_send_list_complete_start(s, m, "ConfbridgeListComplete", total);
+	astman_send_list_complete_end(s);
 
 	return 0;
 }
@@ -2949,12 +2955,8 @@ static int action_confbridgelistrooms(struct mansession *s, const struct message
 	ao2_iterator_destroy(&iter);
 
 	/* Send final confirmation */
-	astman_append(s,
-	"Event: ConfbridgeListRoomsComplete\r\n"
-	"EventList: Complete\r\n"
-	"ListItems: %d\r\n"
-	"%s"
-	"\r\n", totalitems, id_text);
+	astman_send_list_complete_start(s, m, "ConfbridgeListRoomsComplete", totalitems);
+	astman_send_list_complete_end(s);
 	return 0;
 }
 
@@ -3095,7 +3097,7 @@ static int action_confbridgestartrecord(struct mansession *s, const struct messa
 		ast_copy_string(conference->b_profile.rec_file, recordfile, sizeof(conference->b_profile.rec_file));
 	}
 
-	if (start_conf_record_thread(conference)) {
+	if (conf_start_record(conference)) {
 		astman_send_error(s, m, "Internal error starting conference recording.");
 		ao2_unlock(conference);
 		ao2_ref(conference, -1);
diff --git a/apps/app_controlplayback.c b/apps/app_controlplayback.c
index 09815a7..c27fd1c 100644
--- a/apps/app_controlplayback.c
+++ b/apps/app_controlplayback.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 379830 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/pbx.h"
 #include "asterisk/app.h"
diff --git a/apps/app_dahdiras.c b/apps/app_dahdiras.c
index f5890c3..701904b 100644
--- a/apps/app_dahdiras.c
+++ b/apps/app_dahdiras.c
@@ -32,7 +32,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/ioctl.h>
 #include <sys/wait.h>
diff --git a/apps/app_db.c b/apps/app_db.c
index e284867..ef8d0b3 100644
--- a/apps/app_db.c
+++ b/apps/app_db.c
@@ -34,7 +34,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 381366 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/file.h"
 #include "asterisk/channel.h"
diff --git a/apps/app_dial.c b/apps/app_dial.c
index e59a80e..d65dcae 100644
--- a/apps/app_dial.c
+++ b/apps/app_dial.c
@@ -32,7 +32,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 421235 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/time.h>
 #include <sys/signal.h>
@@ -58,7 +58,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 421235 $")
 #include "asterisk/manager.h"
 #include "asterisk/privacy.h"
 #include "asterisk/stringfields.h"
-#include "asterisk/global_datastores.h"
 #include "asterisk/dsp.h"
 #include "asterisk/aoc.h"
 #include "asterisk/ccss.h"
@@ -68,6 +67,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 421235 $")
 #include "asterisk/stasis_channels.h"
 #include "asterisk/bridge_after.h"
 #include "asterisk/features_config.h"
+#include "asterisk/max_forwards.h"
 
 /*** DOCUMENTATION
 	<application name="Dial" language="en_US">
@@ -84,12 +84,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 421235 $")
 				</argument>
 				<argument name="Technology2/Resource2" required="false" multiple="true">
 					<para>Optional extra devices to dial in parallel</para>
-					<para>If you need more then one enter them as
-					Technology2/Resource2&Technology3/Resourse3&.....</para>
+					<para>If you need more than one enter them as
+					Technology2/Resource2&Technology3/Resource3&.....</para>
 				</argument>
 			</parameter>
 			<parameter name="timeout" required="false">
-				<para>Specifies the number of seconds we attempt to dial the specified devices</para>
+				<para>Specifies the number of seconds we attempt to dial the specified devices.</para>
 				<para>If not specified, this defaults to 136 years.</para>
 			</parameter>
 			<parameter name="options" required="false">
@@ -491,7 +491,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 421235 $")
 
 			<para>Unless there is a timeout specified, the Dial application will wait
 			indefinitely until one of the called channels answers, the user hangs up, or
-			if all of the called channels are busy or unavailable. Dialplan executing will
+			if all of the called channels are busy or unavailable. Dialplan execution will
 			continue if no requested channels can be called, or if the timeout expires.
 			This application will report normal termination if the originating channel
 			hangs up, or if the call is bridged and either of the parties in the bridge
@@ -704,6 +704,8 @@ struct chanlist {
 	const char *tech;
 	/*! Channel device addressing.  (Stored in stuff[]) */
 	const char *number;
+	/*! Original channel name.  Must be freed.  Could be NULL if allocation failed. */
+	char *orig_chan_name;
 	uint64_t flags;
 	/*! Saved connected party info from an AST_CONTROL_CONNECTED_LINE. */
 	struct ast_party_connected_line connected;
@@ -722,6 +724,7 @@ static void chanlist_free(struct chanlist *outgoing)
 {
 	ast_party_connected_line_free(&outgoing->connected);
 	ast_aoc_destroy_decoded(outgoing->aoc_s_rate_list);
+	ast_free(outgoing->orig_chan_name);
 	ast_free(outgoing);
 }
 
@@ -872,8 +875,17 @@ static void do_forward(struct chanlist *o, struct cause_args *num,
 		c = o->chan = NULL;
 		cause = AST_CAUSE_BUSY;
 	} else {
+		struct ast_format_cap *nativeformats;
+
+		ast_channel_lock(in);
+		nativeformats = ao2_bump(ast_channel_nativeformats(in));
+		ast_channel_unlock(in);
+
 		/* Setup parameters */
-		c = o->chan = ast_request(tech, ast_channel_nativeformats(in), NULL, in, stuff, &cause);
+		c = o->chan = ast_request(tech, nativeformats, NULL, in, stuff, &cause);
+
+		ao2_cleanup(nativeformats);
+
 		if (c) {
 			if (single && !caller_entertained) {
 				ast_channel_make_compatible(in, o->chan);
@@ -881,6 +893,7 @@ static void do_forward(struct chanlist *o, struct cause_args *num,
 			ast_channel_lock_both(in, o->chan);
 			ast_channel_inherit_variables(in, o->chan);
 			ast_channel_datastore_inherit(in, o->chan);
+			ast_max_forwards_decrement(o->chan);
 			ast_channel_unlock(in);
 			ast_channel_unlock(o->chan);
 			/* When a call is forwarded, we don't want to track new interfaces
@@ -1037,6 +1050,34 @@ static void publish_dial_end_event(struct ast_channel *in, struct dial_head *out
 	}
 }
 
+/*!
+ * \internal
+ * \brief Update connected line on chan from peer.
+ * \since 13.6.0
+ *
+ * \param chan Channel to get connected line updated.
+ * \param peer Channel providing connected line information.
+ * \param is_caller Non-zero if chan is the calling channel.
+ *
+ * \return Nothing
+ */
+static void update_connected_line_from_peer(struct ast_channel *chan, struct ast_channel *peer, int is_caller)
+{
+	struct ast_party_connected_line connected_caller;
+
+	ast_party_connected_line_init(&connected_caller);
+
+	ast_channel_lock(peer);
+	ast_connected_line_copy_from_caller(&connected_caller, ast_channel_caller(peer));
+	ast_channel_unlock(peer);
+	connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
+	if (ast_channel_connected_line_sub(peer, chan, &connected_caller, 0)
+		&& ast_channel_connected_line_macro(peer, chan, &connected_caller, is_caller, 0)) {
+		ast_channel_update_connected_line(chan, &connected_caller, NULL);
+	}
+	ast_party_connected_line_free(&connected_caller);
+}
+
 static struct ast_channel *wait_for_answer(struct ast_channel *in,
 	struct dial_head *out_chans, int *to, struct ast_flags64 *peerflags,
 	char *opt_args[],
@@ -1057,7 +1098,6 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
 	int single = outgoing && !AST_LIST_NEXT(outgoing, node);
 	int caller_entertained = outgoing
 		&& ast_test_flag64(outgoing, OPT_MUSICBACK | OPT_RINGBACK);
-	struct ast_party_connected_line connected_caller;
 	struct ast_str *featurecode = ast_str_alloca(AST_FEATURE_MAX_LEN + 1);
 	int cc_recall_core_id;
 	int is_cc_recall;
@@ -1065,7 +1105,6 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
 	int num_ringing = 0;
 	struct timeval start = ast_tvnow();
 
-	ast_party_connected_line_init(&connected_caller);
 	if (single) {
 		/* Turn off hold music, etc */
 		if (!caller_entertained) {
@@ -1086,15 +1125,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
 
 		if (!ast_test_flag64(outgoing, OPT_IGNORE_CONNECTEDLINE)
 			&& !ast_test_flag64(outgoing, DIAL_CALLERID_ABSENT)) {
-			ast_channel_lock(outgoing->chan);
-			ast_connected_line_copy_from_caller(&connected_caller, ast_channel_caller(outgoing->chan));
-			ast_channel_unlock(outgoing->chan);
-			connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
-			if (ast_channel_connected_line_sub(outgoing->chan, in, &connected_caller, 0) &&
-				ast_channel_connected_line_macro(outgoing->chan, in, &connected_caller, 1, 0)) {
-				ast_channel_update_connected_line(in, &connected_caller, NULL);
-			}
-			ast_party_connected_line_free(&connected_caller);
+			update_connected_line_from_peer(in, outgoing->chan, 1);
 		}
 	}
 
@@ -1148,22 +1179,21 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
 			if (ast_test_flag64(o, DIAL_STILLGOING) && ast_channel_state(c) == AST_STATE_UP) {
 				if (!peer) {
 					ast_verb(3, "%s answered %s\n", ast_channel_name(c), ast_channel_name(in));
-					if (!single && !ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)) {
+					if (o->orig_chan_name
+						&& strcmp(o->orig_chan_name, ast_channel_name(c))) {
+						/*
+						 * The channel name changed so we must generate COLP update.
+						 * Likely because a call pickup channel masqueraded in.
+						 */
+						update_connected_line_from_peer(in, c, 1);
+					} else if (!single && !ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)) {
 						if (o->pending_connected_update) {
 							if (ast_channel_connected_line_sub(c, in, &o->connected, 0) &&
 								ast_channel_connected_line_macro(c, in, &o->connected, 1, 0)) {
 								ast_channel_update_connected_line(in, &o->connected, NULL);
 							}
 						} else if (!ast_test_flag64(o, DIAL_CALLERID_ABSENT)) {
-							ast_channel_lock(c);
-							ast_connected_line_copy_from_caller(&connected_caller, ast_channel_caller(c));
-							ast_channel_unlock(c);
-							connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
-							if (ast_channel_connected_line_sub(c, in, &connected_caller, 0) &&
-								ast_channel_connected_line_macro(c, in, &connected_caller, 1, 0)) {
-								ast_channel_update_connected_line(in, &connected_caller, NULL);
-							}
-							ast_party_connected_line_free(&connected_caller);
+							update_connected_line_from_peer(in, c, 1);
 						}
 					}
 					if (o->aoc_s_rate_list) {
@@ -1218,19 +1248,14 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
 				do_forward(o, &num, peerflags, single, caller_entertained, &orig,
 					forced_clid, stored_clid);
 
-				if (single && o->chan
-					&& !ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)
-					&& !ast_test_flag64(o, DIAL_CALLERID_ABSENT)) {
-					ast_channel_lock(o->chan);
-					ast_connected_line_copy_from_caller(&connected_caller,
-						ast_channel_caller(o->chan));
-					ast_channel_unlock(o->chan);
-					connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
-					if (ast_channel_connected_line_sub(o->chan, in, &connected_caller, 0) &&
-						ast_channel_connected_line_macro(o->chan, in, &connected_caller, 1, 0)) {
-						ast_channel_update_connected_line(in, &connected_caller, NULL);
+				if (o->chan) {
+					ast_free(o->orig_chan_name);
+					o->orig_chan_name = ast_strdup(ast_channel_name(o->chan));
+					if (single
+						&& !ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)
+						&& !ast_test_flag64(o, DIAL_CALLERID_ABSENT)) {
+						update_connected_line_from_peer(in, o->chan, 1);
 					}
-					ast_party_connected_line_free(&connected_caller);
 				}
 				continue;
 			}
@@ -1254,22 +1279,21 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
 					/* This is our guy if someone answered. */
 					if (!peer) {
 						ast_verb(3, "%s answered %s\n", ast_channel_name(c), ast_channel_name(in));
-						if (!single && !ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)) {
+						if (o->orig_chan_name
+							&& strcmp(o->orig_chan_name, ast_channel_name(c))) {
+							/*
+							 * The channel name changed so we must generate COLP update.
+							 * Likely because a call pickup channel masqueraded in.
+							 */
+							update_connected_line_from_peer(in, c, 1);
+						} else if (!single && !ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)) {
 							if (o->pending_connected_update) {
 								if (ast_channel_connected_line_sub(c, in, &o->connected, 0) &&
 									ast_channel_connected_line_macro(c, in, &o->connected, 1, 0)) {
 									ast_channel_update_connected_line(in, &o->connected, NULL);
 								}
 							} else if (!ast_test_flag64(o, DIAL_CALLERID_ABSENT)) {
-								ast_channel_lock(c);
-								ast_connected_line_copy_from_caller(&connected_caller, ast_channel_caller(c));
-								ast_channel_unlock(c);
-								connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
-								if (ast_channel_connected_line_sub(c, in, &connected_caller, 0) &&
-									ast_channel_connected_line_macro(c, in, &connected_caller, 1, 0)) {
-									ast_channel_update_connected_line(in, &connected_caller, NULL);
-								}
-								ast_party_connected_line_free(&connected_caller);
+								update_connected_line_from_peer(in, c, 1);
 							}
 						}
 						if (o->aoc_s_rate_list) {
@@ -2074,7 +2098,6 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
 	);
 	struct ast_flags64 opts = { 0, };
 	char *opt_args[OPT_ARG_ARRAY_SIZE];
-	struct ast_datastore *datastore = NULL;
 	int fulldial = 0, num_dialed = 0;
 	int ignore_cc = 0;
 	char device_name[AST_CHANNEL_NAME];
@@ -2101,6 +2124,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
 	 * \note This will not have any malloced strings so do not free it.
 	 */
 	struct ast_party_caller caller;
+	int max_forwards;
 
 	/* Reset all DIAL variables back to blank, to prevent confusion (in case we don't reset all of them). */
 	ast_channel_lock(chan);
@@ -2111,8 +2135,16 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
 	pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", "");
 	pbx_builtin_setvar_helper(chan, "DIALEDTIME", "");
 	ast_channel_stage_snapshot_done(chan);
+	max_forwards = ast_max_forwards_get(chan);
 	ast_channel_unlock(chan);
 
+	if (max_forwards <= 0) {
+		ast_log(LOG_WARNING, "Cannot place outbound call from channel '%s'. Max forwards exceeded\n",
+				ast_channel_name(chan));
+		pbx_builtin_setvar_helper(chan, "DIALSTATUS", "BUSY");
+		return -1;
+	}
+
 	if (ast_strlen_zero(data)) {
 		ast_log(LOG_WARNING, "Dial requires an argument (technology/resource)\n");
 		pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
@@ -2314,9 +2346,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
 		char *tech = strsep(&number, "/");
 		size_t tech_len;
 		size_t number_len;
-		/* find if we already dialed this interface */
-		struct ast_dialed_interface *di;
-		AST_LIST_HEAD(,ast_dialed_interface) *dialed_interfaces;
+		struct ast_format_cap *nativeformats;
 
 		num_dialed++;
 		if (ast_strlen_zero(number)) {
@@ -2360,7 +2390,6 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
 		/* Request the peer */
 
 		ast_channel_lock(chan);
-		datastore = ast_channel_datastore_find(chan, &dialed_interface_info, NULL);
 		/*
 		 * Seed the chanlist's connected line information with previously
 		 * acquired connected line info from the incoming channel.  The
@@ -2368,64 +2397,15 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
 		 * through the CONNECTED_LINE dialplan function.
 		 */
 		ast_party_connected_line_copy(&tmp->connected, ast_channel_connected(chan));
-		ast_channel_unlock(chan);
 
-		if (datastore)
-			dialed_interfaces = datastore->data;
-		else {
-			if (!(datastore = ast_datastore_alloc(&dialed_interface_info, NULL))) {
-				ast_log(LOG_WARNING, "Unable to create channel datastore for dialed interfaces. Aborting!\n");
-				chanlist_free(tmp);
-				goto out;
-			}
-			datastore->inheritance = DATASTORE_INHERIT_FOREVER;
+		nativeformats = ao2_bump(ast_channel_nativeformats(chan));
 
-			if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
-				ast_datastore_free(datastore);
-				chanlist_free(tmp);
-				goto out;
-			}
-
-			datastore->data = dialed_interfaces;
-			AST_LIST_HEAD_INIT(dialed_interfaces);
-
-			ast_channel_lock(chan);
-			ast_channel_datastore_add(chan, datastore);
-			ast_channel_unlock(chan);
-		}
-
-		AST_LIST_LOCK(dialed_interfaces);
-		AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
-			if (!strcasecmp(di->interface, tmp->interface)) {
-				ast_log(LOG_WARNING, "Skipping dialing interface '%s' again since it has already been dialed\n",
-					di->interface);
-				break;
-			}
-		}
-		AST_LIST_UNLOCK(dialed_interfaces);
-		if (di) {
-			fulldial++;
-			chanlist_free(tmp);
-			continue;
-		}
+		ast_channel_unlock(chan);
 
-		/* It is always ok to dial a Local interface.  We only keep track of
-		 * which "real" interfaces have been dialed.  The Local channel will
-		 * inherit this list so that if it ends up dialing a real interface,
-		 * it won't call one that has already been called. */
-		if (strcasecmp(tmp->tech, "Local")) {
-			if (!(di = ast_calloc(1, sizeof(*di) + strlen(tmp->interface)))) {
-				chanlist_free(tmp);
-				goto out;
-			}
-			strcpy(di->interface, tmp->interface);
+		tc = ast_request(tmp->tech, nativeformats, NULL, chan, tmp->number, &cause);
 
-			AST_LIST_LOCK(dialed_interfaces);
-			AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
-			AST_LIST_UNLOCK(dialed_interfaces);
-		}
+		ao2_cleanup(nativeformats);
 
-		tc = ast_request(tmp->tech, ast_channel_nativeformats(chan), NULL, chan, tmp->number, &cause);
 		if (!tc) {
 			/* If we can't, just go on to the next call */
 			ast_log(LOG_WARNING, "Unable to create channel of type '%s' (cause %d - %s)\n",
@@ -2465,6 +2445,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
 		/* Inherit specially named variables from parent channel */
 		ast_channel_inherit_variables(chan, tc);
 		ast_channel_datastore_inherit(chan, tc);
+		ast_max_forwards_decrement(tc);
 
 		ast_channel_appl_set(tc, "AppDial");
 		ast_channel_data_set(tc, "(Outgoing Line)");
@@ -2557,6 +2538,9 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
 
 		ast_channel_stage_snapshot_done(tc);
 
+		/* Save the original channel name to detect call pickup masquerading in. */
+		tmp->orig_chan_name = ast_strdup(ast_channel_name(tc));
+
 		ast_channel_unlock(tc);
 		ast_channel_unlock(chan);
 
@@ -2680,18 +2664,6 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
 	peer = wait_for_answer(chan, &out_chans, &to, peerflags, opt_args, &pa, &num, &result,
 		dtmf_progress, ignore_cc, &forced_clid, &stored_clid);
 
-	/* The ast_channel_datastore_remove() function could fail here if the
-	 * datastore was moved to another channel during a masquerade. If this is
-	 * the case, don't free the datastore here because later, when the channel
-	 * to which the datastore was moved hangs up, it will attempt to free this
-	 * datastore again, causing a crash
-	 */
-	ast_channel_lock(chan);
-	datastore = ast_channel_datastore_find(chan, &dialed_interface_info, NULL); /* make sure we weren't cleaned up already */
-	if (datastore && !ast_channel_datastore_remove(chan, datastore)) {
-		ast_datastore_free(datastore);
-	}
-	ast_channel_unlock(chan);
 	if (!peer) {
 		if (result) {
 			res = result;
@@ -2702,6 +2674,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
 		}
 	} else {
 		const char *number;
+		int dial_end_raised = 0;
 
 		if (ast_test_flag64(&opts, OPT_CALLER_ANSWER))
 			ast_answer(chan);
@@ -2751,7 +2724,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
 			chans[0] = chan;
 			chans[1] = peer;
 
-			/* we need to stream the announcment while monitoring the caller for a hangup */
+			/* we need to stream the announcement while monitoring the caller for a hangup */
 
 			/* stream the file */
 			res = ast_streamfile(peer, opt_args[OPT_ARG_ANNOUNCE], ast_channel_language(peer));
@@ -2775,34 +2748,50 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
 
 				active_chan = ast_waitfor_n(chans, 2, &ms);
 				if (active_chan) {
+					struct ast_channel *other_chan;
 					struct ast_frame *fr = ast_read(active_chan);
+
 					if (!fr) {
 						ast_autoservice_chan_hangup_peer(chan, peer);
 						res = -1;
 						goto done;
 					}
-					switch(fr->frametype) {
-						case AST_FRAME_DTMF_END:
-							digit = fr->subclass.integer;
-							if (active_chan == peer && strchr(AST_DIGIT_ANY, res)) {
-								ast_stopstream(peer);
-								res = ast_senddigit(chan, digit, 0);
+					switch (fr->frametype) {
+					case AST_FRAME_DTMF_END:
+						digit = fr->subclass.integer;
+						if (active_chan == peer && strchr(AST_DIGIT_ANY, res)) {
+							ast_stopstream(peer);
+							res = ast_senddigit(chan, digit, 0);
+						}
+						break;
+					case AST_FRAME_CONTROL:
+						switch (fr->subclass.integer) {
+						case AST_CONTROL_HANGUP:
+							ast_frfree(fr);
+							ast_autoservice_chan_hangup_peer(chan, peer);
+							res = -1;
+							goto done;
+						case AST_CONTROL_CONNECTED_LINE:
+							/* Pass COLP update to the other channel. */
+							if (active_chan == chan) {
+								other_chan = peer;
+							} else {
+								other_chan = chan;
 							}
-							break;
-						case AST_FRAME_CONTROL:
-							switch (fr->subclass.integer) {
-								case AST_CONTROL_HANGUP:
-									ast_frfree(fr);
-									ast_autoservice_chan_hangup_peer(chan, peer);
-									res = -1;
-									goto done;
-								default:
-									break;
+							if (ast_channel_connected_line_sub(active_chan, other_chan, fr, 1)
+								&& ast_channel_connected_line_macro(active_chan,
+									other_chan, fr, other_chan == chan, 1)) {
+								ast_indicate_data(other_chan, fr->subclass.integer,
+									fr->data.ptr, fr->datalen);
 							}
 							break;
 						default:
-							/* Ignore all others */
 							break;
+						}
+						break;
+					default:
+						/* Ignore all others */
+						break;
 					}
 					ast_frfree(fr);
 				}
@@ -2830,7 +2819,6 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
 			if (ast_pbx_start(peer)) {
 				ast_autoservice_chan_hangup_peer(chan, peer);
 			}
-			hanguptree(&out_chans, NULL, ast_test_flag64(&opts, OPT_CANCEL_ELSEWHERE) ? 1 : 0);
 			if (continue_exec)
 				*continue_exec = 1;
 			res = 0;
@@ -2840,6 +2828,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
 
 		if (ast_test_flag64(&opts, OPT_CALLEE_MACRO) && !ast_strlen_zero(opt_args[OPT_ARG_CALLEE_MACRO])) {
 			const char *macro_result_peer;
+			int macro_res;
 
 			/* Set peer->exten and peer->context so that MACRO_EXTEN and MACRO_CONTEXT get set */
 			ast_channel_lock_both(chan, peer);
@@ -2848,11 +2837,11 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
 			ast_channel_unlock(peer);
 			ast_channel_unlock(chan);
 			ast_replace_subargument_delimiter(opt_args[OPT_ARG_CALLEE_MACRO]);
-			res = ast_app_exec_macro(chan, peer, opt_args[OPT_ARG_CALLEE_MACRO]);
+			macro_res = ast_app_exec_macro(chan, peer, opt_args[OPT_ARG_CALLEE_MACRO]);
 
 			ast_channel_lock(peer);
 
-			if (!res && (macro_result_peer = pbx_builtin_getvar_helper(peer, "MACRO_RESULT"))) {
+			if (!macro_res && (macro_result_peer = pbx_builtin_getvar_helper(peer, "MACRO_RESULT"))) {
 				char *macro_result = ast_strdupa(macro_result_peer);
 				char *macro_transfer_dest;
 
@@ -2861,24 +2850,24 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
 				if (!strcasecmp(macro_result, "BUSY")) {
 					ast_copy_string(pa.status, macro_result, sizeof(pa.status));
 					ast_set_flag64(peerflags, OPT_GO_ON);
-					res = -1;
+					macro_res = -1;
 				} else if (!strcasecmp(macro_result, "CONGESTION") || !strcasecmp(macro_result, "CHANUNAVAIL")) {
 					ast_copy_string(pa.status, macro_result, sizeof(pa.status));
 					ast_set_flag64(peerflags, OPT_GO_ON);
-					res = -1;
+					macro_res = -1;
 				} else if (!strcasecmp(macro_result, "CONTINUE")) {
 					/* hangup peer and keep chan alive assuming the macro has changed
 					   the context / exten / priority or perhaps
 					   the next priority in the current exten is desired.
 					*/
 					ast_set_flag64(peerflags, OPT_GO_ON);
-					res = -1;
+					macro_res = -1;
 				} else if (!strcasecmp(macro_result, "ABORT")) {
 					/* Hangup both ends unless the caller has the g flag */
-					res = -1;
+					macro_res = -1;
 				} else if (!strncasecmp(macro_result, "GOTO:", 5)) {
 					macro_transfer_dest = macro_result + 5;
-					res = -1;
+					macro_res = -1;
 					/* perform a transfer to a new extension */
 					if (strchr(macro_transfer_dest, '^')) { /* context^exten^priority*/
 						ast_replace_subargument_delimiter(macro_transfer_dest);
@@ -2887,17 +2876,21 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
 						ast_set_flag64(peerflags, OPT_GO_ON);
 					}
 				}
-				ast_channel_publish_dial(chan, peer, NULL, macro_result);
+				if (macro_res && !dial_end_raised) {
+					ast_channel_publish_dial(chan, peer, NULL, macro_result);
+					dial_end_raised = 1;
+				}
 			} else {
 				ast_channel_unlock(peer);
 			}
+			res = macro_res;
 		}
 
 		if (ast_test_flag64(&opts, OPT_CALLEE_GOSUB) && !ast_strlen_zero(opt_args[OPT_ARG_CALLEE_GOSUB])) {
 			const char *gosub_result_peer;
 			char *gosub_argstart;
 			char *gosub_args = NULL;
-			int res9 = -1;
+			int gosub_res = -1;
 
 			ast_replace_subargument_delimiter(opt_args[OPT_ARG_CALLEE_GOSUB]);
 			gosub_argstart = strchr(opt_args[OPT_ARG_CALLEE_GOSUB], ',');
@@ -2923,7 +2916,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
 				}
 			}
 			if (gosub_args) {
-				res9 = ast_app_exec_sub(chan, peer, gosub_args, 0);
+				gosub_res = ast_app_exec_sub(chan, peer, gosub_args, 0);
 				ast_free(gosub_args);
 			} else {
 				ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
@@ -2931,7 +2924,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
 
 			ast_channel_lock_both(chan, peer);
 
-			if (!res9 && (gosub_result_peer = pbx_builtin_getvar_helper(peer, "GOSUB_RESULT"))) {
+			if (!gosub_res && (gosub_result_peer = pbx_builtin_getvar_helper(peer, "GOSUB_RESULT"))) {
 				char *gosub_transfer_dest;
 				char *gosub_result = ast_strdupa(gosub_result_peer);
 				const char *gosub_retval = pbx_builtin_getvar_helper(peer, "GOSUB_RETVAL");
@@ -2947,21 +2940,21 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
 				if (!strcasecmp(gosub_result, "BUSY")) {
 					ast_copy_string(pa.status, gosub_result, sizeof(pa.status));
 					ast_set_flag64(peerflags, OPT_GO_ON);
-					res = -1;
+					gosub_res = -1;
 				} else if (!strcasecmp(gosub_result, "CONGESTION") || !strcasecmp(gosub_result, "CHANUNAVAIL")) {
 					ast_copy_string(pa.status, gosub_result, sizeof(pa.status));
 					ast_set_flag64(peerflags, OPT_GO_ON);
-					res = -1;
+					gosub_res = -1;
 				} else if (!strcasecmp(gosub_result, "CONTINUE")) {
 					/* Hangup peer and continue with the next extension priority. */
 					ast_set_flag64(peerflags, OPT_GO_ON);
-					res = -1;
+					gosub_res = -1;
 				} else if (!strcasecmp(gosub_result, "ABORT")) {
 					/* Hangup both ends unless the caller has the g flag */
-					res = -1;
+					gosub_res = -1;
 				} else if (!strncasecmp(gosub_result, "GOTO:", 5)) {
 					gosub_transfer_dest = gosub_result + 5;
-					res = -1;
+					gosub_res = -1;
 					/* perform a transfer to a new extension */
 					if (strchr(gosub_transfer_dest, '^')) { /* context^exten^priority*/
 						ast_replace_subargument_delimiter(gosub_transfer_dest);
@@ -2970,7 +2963,13 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
 						ast_set_flag64(peerflags, OPT_GO_ON);
 					}
 				}
-				ast_channel_publish_dial(chan, peer, NULL, gosub_result);
+				if (gosub_res) {
+					res = gosub_res;
+					if (!dial_end_raised) {
+						ast_channel_publish_dial(chan, peer, NULL, gosub_result);
+						dial_end_raised = 1;
+					}
+				}
 			} else {
 				ast_channel_unlock(peer);
 				ast_channel_unlock(chan);
@@ -2982,7 +2981,10 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
 			/* None of the Dial options changed our status; inform
 			 * everyone that this channel answered
 			 */
-			ast_channel_publish_dial(chan, peer, NULL, "ANSWER");
+			if (!dial_end_raised) {
+				ast_channel_publish_dial(chan, peer, NULL, "ANSWER");
+				dial_end_raised = 1;
+			}
 
 			if (!ast_tvzero(calldurationlimit)) {
 				struct timeval whentohangup = ast_tvadd(ast_tvnow(), calldurationlimit);
diff --git a/apps/app_dictate.c b/apps/app_dictate.c
index be89e81..1dcae7f 100644
--- a/apps/app_dictate.c
+++ b/apps/app_dictate.c
@@ -5,7 +5,7 @@
  *
  * Anthony Minessale II <anthmct at yahoo.com>
  *
- * Donated by Sangoma Technologies <http://www.samgoma.com>
+ * Donated by Sangoma Technologies <http://www.sangoma.com>
  *
  * See http://www.asterisk.org for more information about
  * the Asterisk project. Please do not directly contact
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/stat.h>
 
@@ -175,7 +175,7 @@ static int dictate_exec(struct ast_channel *chan, const char *data)
 				ast_queue_frame(chan, &fr);
 				digit = 0;
 			}
-			if ((f->frametype == AST_FRAME_DTMF)) {
+			if (f->frametype == AST_FRAME_DTMF) {
 				int got = 1;
 				switch(mode) {
 				case DMODE_PLAY:
diff --git a/apps/app_directed_pickup.c b/apps/app_directed_pickup.c
index a88ae0d..fa06595 100644
--- a/apps/app_directed_pickup.c
+++ b/apps/app_directed_pickup.c
@@ -35,7 +35,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 403013 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/file.h"
 #include "asterisk/channel.h"
diff --git a/apps/app_directory.c b/apps/app_directory.c
index 0db54d4..ef3c363 100644
--- a/apps/app_directory.c
+++ b/apps/app_directory.c
@@ -30,7 +30,7 @@
  ***/
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 425384 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <ctype.h>
 
@@ -518,7 +518,9 @@ static struct ast_config *realtime_directory(char *context)
 			/* Skip hidden */
 			continue;
 		}
-		ast_str_set(&tmp, 0, "no-password,%s", S_OR(fullname, ""));
+
+		/* password,Full Name,email,pager,options */
+		ast_str_set(&tmp, 0, "no-password,%s,,,", S_OR(fullname, ""));
 		if (ast_variable_retrieve(rtdata, mailbox, "alias")) {
 			for (alias = ast_variable_browse(rtdata, mailbox); alias; alias = alias->next) {
 				if (!strcasecmp(alias->name, "alias")) {
@@ -602,7 +604,10 @@ static int search_directory_sub(const char *context, struct ast_config *vmcfg, s
 {
 	struct ast_variable *v;
 	struct ast_str *buf = ast_str_thread_get(&commonbuf, 100);
-	char *pos, *bufptr, *cat, *alias;
+	char *name;
+	char *options;
+	char *alias;
+	char *cat;
 	struct directory_item *item;
 	int res;
 
@@ -613,33 +618,36 @@ static int search_directory_sub(const char *context, struct ast_config *vmcfg, s
 	ast_debug(2, "Pattern: %s\n", ext);
 
 	for (v = ast_variable_browse(vmcfg, context); v; v = v->next) {
-
-		/* Ignore hidden */
-		if (strcasestr(v->value, "hidefromdir=yes")) {
-			continue;
-		}
-
 		ast_str_set(&buf, 0, "%s", v->value);
-		bufptr = ast_str_buffer(buf);
+		options = ast_str_buffer(buf);
 
 		/* password,Full Name,email,pager,options */
-		strsep(&bufptr, ",");
-		pos = strsep(&bufptr, ",");
-
-		/* No name to compare against */
-		if (ast_strlen_zero(pos)) {
+		strsep(&options, ",");          /* Skip password */
+		name = strsep(&options, ",");   /* Save full name */
+		strsep(&options, ",");          /* Skip email */
+		strsep(&options, ",");          /* Skip pager */
+		/* options is now the options field if it exists. */
+
+		if (options && strcasestr(options, "hidefromdir=yes")) {
+			/* Ignore hidden */
+			continue;
+		}
+		if (ast_strlen_zero(name)) {
+			/* No name to compare against */
 			continue;
 		}
 
 		res = 0;
 		if (ast_test_flag(&flags, OPT_LISTBYLASTNAME)) {
-			res = check_match(&item, context, pos, v->name, ext, 0 /* use_first_name */);
+			res = check_match(&item, context, name, v->name, ext, 0 /* use_first_name */);
 		}
 		if (!res && ast_test_flag(&flags, OPT_LISTBYFIRSTNAME)) {
-			res = check_match(&item, context, pos, v->name, ext, 1 /* use_first_name */);
+			res = check_match(&item, context, name, v->name, ext, 1 /* use_first_name */);
 		}
-		if (!res && ast_test_flag(&flags, OPT_ALIAS) && (alias = strcasestr(bufptr, "alias="))) {
+		if (!res && ast_test_flag(&flags, OPT_ALIAS)
+			&& options && (alias = strcasestr(options, "alias="))) {
 			char *a;
+
 			ast_debug(1, "Found alias: %s\n", alias);
 			while ((a = strsep(&alias, "|"))) {
 				if (!strncasecmp(a, "alias=", 6)) {
@@ -683,9 +691,9 @@ static int search_directory_sub(const char *context, struct ast_config *vmcfg, s
 				res = check_match(&item, context, position, cat, ext, 1 /* use_first_name */);
 			}
 			if (!res && ast_test_flag(&flags, OPT_ALIAS)) {
-				struct ast_variable *alias;
-				for (alias = ast_variable_browse(ucfg, cat); alias; alias = alias->next) {
-					if (!strcasecmp(v->name, "alias") && (res = check_match(&item, context, v->value, cat, ext, 1))) {
+				for (v = ast_variable_browse(ucfg, cat); v; v = v->next) {
+					if (!strcasecmp(v->name, "alias")
+						&& (res = check_match(&item, context, v->value, cat, ext, 1))) {
 						break;
 					}
 				}
diff --git a/apps/app_disa.c b/apps/app_disa.c
index d1c4336..824e8fe 100644
--- a/apps/app_disa.c
+++ b/apps/app_disa.c
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 404295 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <math.h>
 #include <sys/time.h>
diff --git a/apps/app_dumpchan.c b/apps/app_dumpchan.c
index dd009e3..1a5a446 100644
--- a/apps/app_dumpchan.c
+++ b/apps/app_dumpchan.c
@@ -34,7 +34,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419044 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/pbx.h"
 #include "asterisk/module.h"
@@ -72,7 +72,7 @@ static int serialize_showchan(struct ast_channel *c, char *buf, size_t size)
 {
 	long elapsed_seconds = 0;
 	int hour = 0, min = 0, sec = 0;
-	struct ast_str *format_buf = ast_str_alloca(64);
+	struct ast_str *format_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 	char cgrp[256];
 	char pgrp[256];
 	struct ast_str *write_transpath = ast_str_alloca(256);
diff --git a/apps/app_echo.c b/apps/app_echo.c
index 7b13862..d8b2079 100644
--- a/apps/app_echo.c
+++ b/apps/app_echo.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419044 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/file.h"
 #include "asterisk/module.h"
diff --git a/apps/app_exec.c b/apps/app_exec.c
index 13d36a5..ab9a9ae 100644
--- a/apps/app_exec.c
+++ b/apps/app_exec.c
@@ -31,7 +31,7 @@
  ***/
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 328259 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/file.h"
 #include "asterisk/channel.h"
diff --git a/apps/app_externalivr.c b/apps/app_externalivr.c
index 1affa18..9cb8839 100644
--- a/apps/app_externalivr.c
+++ b/apps/app_externalivr.c
@@ -37,7 +37,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <signal.h>
 
@@ -519,6 +519,8 @@ static int app_exec(struct ast_channel *chan, const char *data)
 			break;
 		}
 
+		ast_free(addrs);
+
 		if (i == num_addrs) {
 			ast_chan_log(LOG_ERROR, chan, "Could not connect to any host.  ExternalIVR failed.\n");
 			goto exit;
diff --git a/apps/app_fax.c b/apps/app_fax.c
index 62e1452..7822a34 100644
--- a/apps/app_fax.c
+++ b/apps/app_fax.c
@@ -21,7 +21,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <string.h>
 #include <stdlib.h>
diff --git a/apps/app_festival.c b/apps/app_festival.c
index abee1c3..7df8caf 100644
--- a/apps/app_festival.c
+++ b/apps/app_festival.c
@@ -42,7 +42,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/socket.h>
 #include <netdb.h>
diff --git a/apps/app_flash.c b/apps/app_flash.c
index 199878e..dbbad33 100644
--- a/apps/app_flash.c
+++ b/apps/app_flash.c
@@ -32,7 +32,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 357721 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <dahdi/user.h>
 
diff --git a/apps/app_followme.c b/apps/app_followme.c
index e4c04cc..e5a5ee3 100644
--- a/apps/app_followme.c
+++ b/apps/app_followme.c
@@ -41,7 +41,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <signal.h>
 
@@ -64,6 +64,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
 #include "asterisk/dsp.h"
 #include "asterisk/app.h"
 #include "asterisk/stasis_channels.h"
+#include "asterisk/max_forwards.h"
 
 /*** DOCUMENTATION
 	<application name="FollowMe" language="en_US">
@@ -1069,6 +1070,7 @@ static struct ast_channel *findmeexec(struct fm_args *tpargs, struct ast_channel
 			ast_connected_line_copy_from_caller(ast_channel_connected(outbound), ast_channel_caller(caller));
 			ast_channel_inherit_variables(caller, outbound);
 			ast_channel_datastore_inherit(caller, outbound);
+			ast_max_forwards_decrement(outbound);
 			ast_channel_language_set(outbound, ast_channel_language(caller));
 			ast_channel_req_accountcodes(outbound, caller, AST_CHANNEL_REQUESTOR_BRIDGE_PEER);
 			ast_channel_musicclass_set(outbound, ast_channel_musicclass(caller));
@@ -1304,12 +1306,23 @@ static int app_exec(struct ast_channel *chan, const char *data)
 		AST_APP_ARG(options);
 	);
 	char *opt_args[FOLLOWMEFLAG_ARG_ARRAY_SIZE];
+	int max_forwards;
 
 	if (ast_strlen_zero(data)) {
 		ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app);
 		return -1;
 	}
 
+	ast_channel_lock(chan);
+	max_forwards = ast_max_forwards_get(chan);
+	ast_channel_unlock(chan);
+
+	if (max_forwards <= 0) {
+		ast_log(LOG_WARNING, "Unable to execute FollowMe on channel %s. Max forwards exceeded\n",
+				ast_channel_name(chan));
+		return -1;
+	}
+
 	argstr = ast_strdupa((char *) data);
 
 	AST_STANDARD_APP_ARGS(args, argstr);
diff --git a/apps/app_forkcdr.c b/apps/app_forkcdr.c
index ad060c5..bbd1588 100644
--- a/apps/app_forkcdr.c
+++ b/apps/app_forkcdr.c
@@ -32,7 +32,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420124 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/file.h"
 #include "asterisk/channel.h"
diff --git a/apps/app_getcpeid.c b/apps/app_getcpeid.c
index 6f3029f..319ed3c 100644
--- a/apps/app_getcpeid.c
+++ b/apps/app_getcpeid.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/lock.h"
 #include "asterisk/file.h"
@@ -87,9 +87,8 @@ static int cpeid_exec(struct ast_channel *chan, const char *idata)
 		res = ast_adsi_get_cpeid(chan, cpeid, 0);
 		if (res > 0) {
 			gotcpeid = 1;
-			ast_verb(3, "Got CPEID of '%02x:%02x:%02x:%02x' on '%s'\n",
-				(unsigned)cpeid[0], (unsigned)cpeid[1], (unsigned)cpeid[2],
-				(unsigned)cpeid[3], ast_channel_name(chan));
+			ast_verb(3, "Got CPEID of '%02hhx:%02hhx:%02hhx:%02hhx' on '%s'\n",
+				cpeid[0], cpeid[1], cpeid[2], cpeid[3], ast_channel_name(chan));
 		}
 		if (res > -1) {
 			strcpy(data[1], "Measuring CPE...");
@@ -103,9 +102,8 @@ static int cpeid_exec(struct ast_channel *chan, const char *idata)
 		}
 		if (res > -1) {
 			if (gotcpeid)
-				snprintf(data[1], 80, "CPEID: %02x:%02x:%02x:%02x",
-					(unsigned)cpeid[0], (unsigned)cpeid[1],
-					(unsigned)cpeid[2], (unsigned)cpeid[3]);
+				snprintf(data[1], 80, "CPEID: %02hhx:%02hhx:%02hhx:%02hhx",
+					cpeid[0], cpeid[1], cpeid[2], cpeid[3]);
 			else
 				strcpy(data[1], "CPEID Unknown");
 			if (gotgeometry) 
diff --git a/apps/app_ices.c b/apps/app_ices.c
index c4d90ef..d0fdf5c 100644
--- a/apps/app_ices.c
+++ b/apps/app_ices.c
@@ -33,7 +33,7 @@
  
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <signal.h>
 #include <fcntl.h>
diff --git a/apps/app_image.c b/apps/app_image.c
index c03976a..4218917 100644
--- a/apps/app_image.c
+++ b/apps/app_image.c
@@ -31,7 +31,7 @@
  
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/pbx.h"
 #include "asterisk/module.h"
diff --git a/apps/app_ivrdemo.c b/apps/app_ivrdemo.c
index 4f0913a..de59fbd 100644
--- a/apps/app_ivrdemo.c
+++ b/apps/app_ivrdemo.c
@@ -32,7 +32,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/file.h"
 #include "asterisk/channel.h"
diff --git a/apps/app_jack.c b/apps/app_jack.c
index 7f37e24..8d40431 100644
--- a/apps/app_jack.c
+++ b/apps/app_jack.c
@@ -42,7 +42,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <limits.h>
 
diff --git a/apps/app_macro.c b/apps/app_macro.c
index 3ccc183..d8fd47f 100644
--- a/apps/app_macro.c
+++ b/apps/app_macro.c
@@ -32,7 +32,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 422719 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/file.h"
 #include "asterisk/channel.h"
@@ -376,9 +376,10 @@ static int _macro_exec(struct ast_channel *chan, const char *data, int exclusive
 		pbx_builtin_setvar_helper(chan, varname, cur);
 		argc++;
 	}
-	ast_channel_unlock(chan);
 	autoloopflag = ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
 	ast_set_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
+	ast_channel_unlock(chan);
+
 	while (ast_exists_extension(chan, ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan),
 		S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
 		struct ast_context *c;
@@ -503,7 +504,10 @@ static int _macro_exec(struct ast_channel *chan, const char *data, int exclusive
 
 		/* don't stop executing extensions when we're in "h" */
 		if (ast_check_hangup(chan) && !inhangup) {
-			ast_debug(1, "Extension %s, macroexten %s, priority %d returned normally even though call was hung up\n", ast_channel_exten(chan), ast_channel_macroexten(chan), ast_channel_priority(chan));
+			ast_debug(1, "Extension %s, macroexten %s, priority %d returned normally even though call was hung up\n",
+				ast_channel_exten(chan),
+				ast_channel_macroexten(chan),
+				ast_channel_priority(chan));
 			goto out;
 		}
 		ast_channel_priority_set(chan, ast_channel_priority(chan) + 1);
@@ -522,24 +526,17 @@ static int _macro_exec(struct ast_channel *chan, const char *data, int exclusive
   	for (x = 1; x < argc; x++) {
   		/* Restore old arguments and delete ours */
 		snprintf(varname, sizeof(varname), "ARG%d", x);
-  		if (oldargs[x]) {
-			pbx_builtin_setvar_helper(chan, varname, oldargs[x]);
-			ast_free(oldargs[x]);
-		} else {
-			pbx_builtin_setvar_helper(chan, varname, NULL);
-		}
+		pbx_builtin_setvar_helper(chan, varname, oldargs[x]);
+		ast_free(oldargs[x]);
   	}
 
 	/* Restore macro variables */
 	pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", save_macro_exten);
 	pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", save_macro_context);
 	pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", save_macro_priority);
-	if (save_macro_exten)
-		ast_free(save_macro_exten);
-	if (save_macro_context)
-		ast_free(save_macro_context);
-	if (save_macro_priority)
-		ast_free(save_macro_priority);
+	ast_free(save_macro_exten);
+	ast_free(save_macro_context);
+	ast_free(save_macro_priority);
 
 	if (setmacrocontext) {
 		ast_channel_macrocontext_set(chan, "");
@@ -547,7 +544,8 @@ static int _macro_exec(struct ast_channel *chan, const char *data, int exclusive
 		ast_channel_macropriority_set(chan, 0);
 	}
 
-	if (!strcasecmp(ast_channel_context(chan), fullmacro)) {
+	if (!strcasecmp(ast_channel_context(chan), fullmacro)
+		&& !(ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO)) {
 		const char *offsets;
 
   		/* If we're leaving the macro normally, restore original information */
@@ -568,8 +566,7 @@ static int _macro_exec(struct ast_channel *chan, const char *data, int exclusive
 	}
 
 	pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", save_macro_offset);
-	if (save_macro_offset)
-		ast_free(save_macro_offset);
+	ast_free(save_macro_offset);
 
 	/* Unlock the macro */
 	if (exclusive) {
diff --git a/apps/app_meetme.c b/apps/app_meetme.c
index 0e708bf..b2131ff 100644
--- a/apps/app_meetme.c
+++ b/apps/app_meetme.c
@@ -47,7 +47,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 429029 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <dahdi/user.h>
 
@@ -5577,13 +5577,10 @@ static int action_meetmelist(struct mansession *s, const struct message *m)
 		ao2_iterator_destroy(&user_iter);
 	}
 	AST_LIST_UNLOCK(&confs);
+
 	/* Send final confirmation */
-	astman_append(s,
-	"Event: MeetmeListComplete\r\n"
-	"EventList: Complete\r\n"
-	"ListItems: %d\r\n"
-	"%s"
-	"\r\n", total, idText);
+	astman_send_list_complete_start(s, m, "MeetmeListComplete", total);
+	astman_send_list_complete_end(s);
 	return 0;
 }
 
@@ -5645,12 +5642,8 @@ static int action_meetmelistrooms(struct mansession *s, const struct message *m)
 	AST_LIST_UNLOCK(&confs);
 
 	/* Send final confirmation */
-	astman_append(s,
-	"Event: MeetmeListRoomsComplete\r\n"
-	"EventList: Complete\r\n"
-	"ListItems: %d\r\n"
-	"%s"
-	"\r\n", totalitems, idText);
+	astman_send_list_complete_start(s, m, "MeetmeListRoomsComplete", totalitems);
+	astman_send_list_complete_end(s);
 	return 0;
 }
 
@@ -5781,6 +5774,9 @@ static void meetme_set_defaults(void)
 
 	/*  Logging of participants defaults to ON for compatibility reasons */
 	rt_log_members = 1;
+
+	/* Set default number of buffers to be allocated. */
+	audio_buffers = DEFAULT_AUDIO_BUFFERS;
 }
 
 static void load_config_meetme(int reload)
@@ -7546,14 +7542,13 @@ static int sla_build_trunk(struct ast_config *cfg, const char *cat)
 	ao2_unlock(trunk);
 
 	if (!ast_strlen_zero(trunk->autocontext)) {
-		struct ast_context *context;
-		context = ast_context_find_or_create(NULL, NULL, trunk->autocontext, sla_registrar);
-		if (!context) {
+		if (!ast_context_find_or_create(NULL, NULL, trunk->autocontext, sla_registrar)) {
 			ast_log(LOG_ERROR, "Failed to automatically find or create "
 				"context '%s' for SLA!\n", trunk->autocontext);
 			return -1;
 		}
-		if (ast_add_extension2(context, 0 /* don't replace */, "s", 1,
+
+		if (ast_add_extension(trunk->autocontext, 0 /* don't replace */, "s", 1,
 			NULL, NULL, slatrunk_app, ast_strdup(trunk->name), ast_free_ptr, sla_registrar)) {
 			ast_log(LOG_ERROR, "Failed to automatically create extension "
 				"for trunk '%s'!\n", trunk->name);
@@ -7722,17 +7717,16 @@ static int sla_build_station(struct ast_config *cfg, const char *cat)
 	ao2_unlock(station);
 
 	if (!ast_strlen_zero(station->autocontext)) {
-		struct ast_context *context;
 		struct sla_trunk_ref *trunk_ref;
-		context = ast_context_find_or_create(NULL, NULL, station->autocontext, sla_registrar);
-		if (!context) {
+
+		if (!ast_context_find_or_create(NULL, NULL, station->autocontext, sla_registrar)) {
 			ast_log(LOG_ERROR, "Failed to automatically find or create "
 				"context '%s' for SLA!\n", station->autocontext);
 			return -1;
 		}
 		/* The extension for when the handset goes off-hook.
 		 * exten => station1,1,SLAStation(station1) */
-		if (ast_add_extension2(context, 0 /* don't replace */, station->name, 1,
+		if (ast_add_extension(station->autocontext, 0 /* don't replace */, station->name, 1,
 			NULL, NULL, slastation_app, ast_strdup(station->name), ast_free_ptr, sla_registrar)) {
 			ast_log(LOG_ERROR, "Failed to automatically create extension "
 				"for trunk '%s'!\n", station->name);
@@ -7745,7 +7739,7 @@ static int sla_build_station(struct ast_config *cfg, const char *cat)
 			snprintf(hint, sizeof(hint), "SLA:%s", exten);
 			/* Extension for this line button 
 			 * exten => station1_line1,1,SLAStation(station1_line1) */
-			if (ast_add_extension2(context, 0 /* don't replace */, exten, 1,
+			if (ast_add_extension(station->autocontext, 0 /* don't replace */, exten, 1,
 				NULL, NULL, slastation_app, ast_strdup(exten), ast_free_ptr, sla_registrar)) {
 				ast_log(LOG_ERROR, "Failed to automatically create extension "
 					"for trunk '%s'!\n", station->name);
@@ -7753,7 +7747,7 @@ static int sla_build_station(struct ast_config *cfg, const char *cat)
 			}
 			/* Hint for this line button 
 			 * exten => station1_line1,hint,SLA:station1_line1 */
-			if (ast_add_extension2(context, 0 /* don't replace */, exten, PRIORITY_HINT,
+			if (ast_add_extension(station->autocontext, 0 /* don't replace */, exten, PRIORITY_HINT,
 				NULL, NULL, hint, NULL, NULL, sla_registrar)) {
 				ast_log(LOG_ERROR, "Failed to automatically create hint "
 					"for trunk '%s'!\n", station->name);
diff --git a/apps/app_milliwatt.c b/apps/app_milliwatt.c
index 2c3baf1..a350958 100644
--- a/apps/app_milliwatt.c
+++ b/apps/app_milliwatt.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419044 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/channel.h"
diff --git a/apps/app_minivm.c b/apps/app_minivm.c
index f50b93d..45d04d8 100644
--- a/apps/app_minivm.c
+++ b/apps/app_minivm.c
@@ -146,7 +146,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <ctype.h>
 #include <sys/time.h>
@@ -1366,7 +1366,7 @@ static int sendmail(struct minivm_template *template, struct minivm_account *vmu
 	prep_email_sub_vars(ast, vmu, cidnum, cidname, dur, date, counter);
 
 	/* Find email address to use */
-	/* If there's a server e-mail adress in the account, user that, othterwise template */
+	/* If there's a server e-mail address in the account, use that, othterwise template */
 	fromemail = ast_strlen_zero(vmu->serveremail) ?  template->serveremail : vmu->serveremail;
 
 	/* Find name to user for server e-mail */
@@ -1838,7 +1838,8 @@ static int notify_new_message(struct ast_channel *chan, const char *templatename
 		etemplate = message_template_find(vmu->ptemplate);
 		if (!etemplate)
 			etemplate = message_template_find("pager-default");
-		if (etemplate->locale) {
+
+		if (!ast_strlen_zero(etemplate->locale)) {
 			ast_copy_string(oldlocale, setlocale(LC_TIME, ""), sizeof(oldlocale));
 			setlocale(LC_TIME, etemplate->locale);
 		}
@@ -1867,9 +1868,8 @@ static int notify_new_message(struct ast_channel *chan, const char *templatename
 
 notify_cleanup:
 	run_externnotify(chan, vmu);		/* Run external notification */
-
-	if (etemplate->locale) {
-		setlocale(LC_TIME, oldlocale); /* Rest to old locale */
+	if (!ast_strlen_zero(etemplate->locale)) {
+		setlocale(LC_TIME, oldlocale);	/* Reset to old locale */
 	}
 	return res;
 }
diff --git a/apps/app_mixmonitor.c b/apps/app_mixmonitor.c
index 90bfee5..e6f4053 100644
--- a/apps/app_mixmonitor.c
+++ b/apps/app_mixmonitor.c
@@ -39,7 +39,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 424507 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/paths.h"	/* use ast_config_AST_MONITOR_DIR */
 #include "asterisk/stringfields.h"
@@ -727,12 +727,6 @@ static void *mixmonitor_thread(void *obj)
 		ast_audiohook_lock(&mixmonitor->audiohook);
 	}
 
-	/* Test Event */
-	ast_test_suite_event_notify("MIXMONITOR_END", "Channel: %s\r\n"
-									"File: %s\r\n",
-									ast_channel_name(mixmonitor->autochan->chan),
-									mixmonitor->filename);
-
 	ast_audiohook_unlock(&mixmonitor->audiohook);
 
 	ast_channel_lock(mixmonitor->autochan->chan);
@@ -760,6 +754,7 @@ static void *mixmonitor_thread(void *obj)
 	}
 
 	ast_verb(2, "End MixMonitor Recording %s\n", mixmonitor->name);
+	ast_test_suite_event_notify("MIXMONITOR_END", "File: %s\r\n", mixmonitor->filename);
 
 	if (!AST_LIST_EMPTY(&mixmonitor->recipient_list)) {
 		if (ast_strlen_zero(fs_ext)) {
diff --git a/apps/app_morsecode.c b/apps/app_morsecode.c
index 9210c54..27e70f0 100644
--- a/apps/app_morsecode.c
+++ b/apps/app_morsecode.c
@@ -30,7 +30,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/file.h"
 #include "asterisk/channel.h"
diff --git a/apps/app_mp3.c b/apps/app_mp3.c
index e6a0462..a22265d 100644
--- a/apps/app_mp3.c
+++ b/apps/app_mp3.c
@@ -34,7 +34,7 @@
  
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/time.h>
 #include <signal.h>
diff --git a/apps/app_nbscat.c b/apps/app_nbscat.c
index 5eafe2e..a347cf3 100644
--- a/apps/app_nbscat.c
+++ b/apps/app_nbscat.c
@@ -31,7 +31,7 @@
  
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <fcntl.h>
 #include <sys/time.h>
diff --git a/apps/app_originate.c b/apps/app_originate.c
index cced3ad..15898b2 100644
--- a/apps/app_originate.c
+++ b/apps/app_originate.c
@@ -38,7 +38,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419044 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/file.h"
 #include "asterisk/channel.h"
diff --git a/apps/app_osplookup.c b/apps/app_osplookup.c
index abdfa12..ef816c3 100644
--- a/apps/app_osplookup.c
+++ b/apps/app_osplookup.c
@@ -36,7 +36,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <osp/osp.h>
 #include <osp/osputils.h>
@@ -1425,7 +1425,9 @@ static int osp_uuid2str(
 	int res;
 
 	if ((uuid != NULL) && (bufsize > OSP_SIZE_UUIDSTR)) {
-		snprintf(buffer, bufsize, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+		snprintf(buffer, bufsize, "%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-"
+					  "%02hhx%02hhx-%02hhx%02hhx-"
+					  "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx",
 			uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
 			uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]);
 		res = OSP_OK;
diff --git a/apps/app_page.c b/apps/app_page.c
index e31cb78..590b15b 100644
--- a/apps/app_page.c
+++ b/apps/app_page.c
@@ -32,7 +32,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 410158 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/channel.h"
 #include "asterisk/pbx.h"
@@ -60,7 +60,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 410158 $")
 				<argument name="Technology2/Resource2" multiple="true">
 					<para>Optional extra devices to dial in parallel</para>
 					<para>If you need more than one, enter them as Technology2/Resource2&
-					Technology3/Resourse3&.....</para>
+					Technology3/Resource3&.....</para>
 				</argument>
 			</parameter>
 			<parameter name="options">
@@ -249,12 +249,18 @@ static void page_state_callback(struct ast_dial *dial)
 
 static int page_exec(struct ast_channel *chan, const char *data)
 {
-	char *tech, *resource, *tmp;
-	char confbridgeopts[128], originator[AST_CHANNEL_NAME];
+	char *tech;
+	char *resource;
+	char *tmp;
+	char *predial_callee = NULL;
+	char confbridgeopts[128];
+	char originator[AST_CHANNEL_NAME];
 	struct page_options options = { { 0, }, { 0, } };
 	unsigned int confid = ast_random();
 	struct ast_app *app;
-	int res = 0, pos = 0, i = 0;
+	int res = 0;
+	int pos = 0;
+	int i = 0;
 	struct ast_dial **dial_list;
 	unsigned int num_dials;
 	int timeout = 0;
@@ -310,6 +316,15 @@ static int page_exec(struct ast_channel *chan, const char *data)
 		return -1;
 	}
 
+	/* PREDIAL: Preprocess any callee gosub arguments. */
+	if (ast_test_flag(&options.flags, PAGE_PREDIAL_CALLEE)
+		&& !ast_strlen_zero(options.opts[OPT_ARG_PREDIAL_CALLEE])) {
+		ast_replace_subargument_delimiter(options.opts[OPT_ARG_PREDIAL_CALLEE]);
+		predial_callee =
+			(char *) ast_app_expand_sub_args(chan, options.opts[OPT_ARG_PREDIAL_CALLEE]);
+	}
+
+	/* PREDIAL: Run gosub on the caller's channel */
 	if (ast_test_flag(&options.flags, PAGE_PREDIAL_CALLER)
 		&& !ast_strlen_zero(options.opts[OPT_ARG_PREDIAL_CALLER])) {
 		ast_replace_subargument_delimiter(options.opts[OPT_ARG_PREDIAL_CALLER]);
@@ -360,9 +375,8 @@ static int page_exec(struct ast_channel *chan, const char *data)
 		/* Set ANSWER_EXEC as global option */
 		ast_dial_option_global_enable(dial, AST_DIAL_OPTION_ANSWER_EXEC, confbridgeopts);
 
-		if (ast_test_flag(&options.flags, PAGE_PREDIAL_CALLEE)
-			&& !ast_strlen_zero(options.opts[OPT_ARG_PREDIAL_CALLEE])) {
-			ast_dial_option_global_enable(dial, AST_DIAL_OPTION_PREDIAL, options.opts[OPT_ARG_PREDIAL_CALLEE]);
+		if (predial_callee) {
+			ast_dial_option_global_enable(dial, AST_DIAL_OPTION_PREDIAL, predial_callee);
 		}
 
 		if (timeout) {
@@ -383,6 +397,8 @@ static int page_exec(struct ast_channel *chan, const char *data)
 		dial_list[pos++] = dial;
 	}
 
+	ast_free(predial_callee);
+
 	if (!ast_test_flag(&options.flags, PAGE_QUIET)) {
 		res = ast_streamfile(chan, "beep", ast_channel_language(chan));
 		if (!res)
diff --git a/apps/app_playback.c b/apps/app_playback.c
index cded2cb..e5df794 100644
--- a/apps/app_playback.c
+++ b/apps/app_playback.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/file.h"
 #include "asterisk/pbx.h"
@@ -490,7 +490,9 @@ static int playback_exec(struct ast_channel *chan, const char *data)
 				ast_stopstream(chan);
 			}
 			if (res) {
-				ast_log(LOG_WARNING, "Playback failed on %s for %s\n", ast_channel_name(chan), (char *)data);
+				if (!ast_check_hangup(chan)) {
+					ast_log(LOG_WARNING, "Playback failed on %s for %s\n", ast_channel_name(chan), (char *)data);
+				}
 				res = 0;
 				mres = 1;
 			}
diff --git a/apps/app_playtones.c b/apps/app_playtones.c
index c2c307e..fd947dd 100644
--- a/apps/app_playtones.c
+++ b/apps/app_playtones.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 356042 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/pbx.h"
diff --git a/apps/app_privacy.c b/apps/app_privacy.c
index 190463e..0e04df6 100644
--- a/apps/app_privacy.c
+++ b/apps/app_privacy.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 357542 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/lock.h"
 #include "asterisk/file.h"
diff --git a/apps/app_queue.c b/apps/app_queue.c
index cd3b0b2..5a8dcd2 100644
--- a/apps/app_queue.c
+++ b/apps/app_queue.c
@@ -69,7 +69,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428687 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/time.h>
 #include <sys/signal.h>
@@ -98,7 +98,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428687 $")
 #include "asterisk/stringfields.h"
 #include "asterisk/astobj2.h"
 #include "asterisk/strings.h"
-#include "asterisk/global_datastores.h"
 #include "asterisk/taskprocessor.h"
 #include "asterisk/aoc.h"
 #include "asterisk/callerid.h"
@@ -113,6 +112,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428687 $")
 #include "asterisk/mixmonitor.h"
 #include "asterisk/core_unreal.h"
 #include "asterisk/bridge_basic.h"
+#include "asterisk/max_forwards.h"
 
 /*!
  * \par Please read before modifying this file.
@@ -542,7 +542,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428687 $")
 			Count number of members answering a queue.
 		</synopsis>
 		<syntax>
-			<parameter name="queuename" required="true" />
+			<parameter name="queuename" required="false" />
 			<parameter name="option" required="true">
 				<enumlist>
 					<enum name="logged">
@@ -558,13 +558,22 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428687 $")
 						<para>Returns the total number of members for the specified queue.</para>
 					</enum>
 					<enum name="penalty">
-						<para>Gets or sets queue member penalty.</para>
+						<para>Gets or sets queue member penalty.  If
+						<replaceable>queuename</replaceable> is not specified
+						when setting the penalty then the penalty is set in all queues
+						the interface is a member.</para>
 					</enum>
 					<enum name="paused">
-						<para>Gets or sets queue member paused status.</para>
+						<para>Gets or sets queue member paused status.  If
+						<replaceable>queuename</replaceable> is not specified
+						when setting the paused status then the paused status is set
+						in all queues the interface is a member.</para>
 					</enum>
 					<enum name="ringinuse">
-						<para>Gets or sets queue member ringinuse.</para>
+						<para>Gets or sets queue member ringinuse.  If
+						<replaceable>queuename</replaceable> is not specified
+						when setting ringinuse then ringinuse is set
+						in all queues the interface is a member.</para>
 					</enum>
 				</enumlist>
 			</parameter>
@@ -572,10 +581,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428687 $")
 		</syntax>
 		<description>
 			<para>Allows access to queue counts [R] and member information [R/W].</para>
-			<para>
-				<replaceable>queuename</replaceable> is required for all operations
-				<replaceable>interface</replaceable> is required for all member operations.
-			</para>
+			<para><replaceable>queuename</replaceable> is required for all read operations.</para>
+			<para><replaceable>interface</replaceable> is required for all member operations.</para>
 		</description>
 		<see-also>
 			<ref type="application">Queue</ref>
@@ -1439,6 +1446,8 @@ struct callattempt {
 	/*! TRUE if the call is still active */
 	unsigned int stillgoing:1;
 	struct ast_aoc_decoded *aoc_s_rate_list;
+	/*! Original channel name.  Must be freed.  Could be NULL if allocation failed. */
+	char *orig_chan_name;
 };
 
 
@@ -3683,7 +3692,8 @@ static int valid_exit(struct queue_ent *qe, char digit)
 
 static int say_position(struct queue_ent *qe, int ringing)
 {
-	int res = 0, avgholdmins, avgholdsecs, announceposition = 0;
+	int res = 0, announceposition = 0;
+	long avgholdmins, avgholdsecs;
 	int say_thanks = 1;
 	time_t now;
 
@@ -3757,23 +3767,23 @@ static int say_position(struct queue_ent *qe, int ringing)
 		}
 	}
 	/* Round hold time to nearest minute */
-	avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);
+	avgholdmins = labs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);
 
 	/* If they have specified a rounding then round the seconds as well */
 	if (qe->parent->roundingseconds) {
-		avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
+		avgholdsecs = (labs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
 		avgholdsecs *= qe->parent->roundingseconds;
 	} else {
 		avgholdsecs = 0;
 	}
 
-	ast_verb(3, "Hold time for %s is %d minute(s) %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
+	ast_verb(3, "Hold time for %s is %ld minute(s) %ld seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
 
 	/* If the hold time is >1 min, if it's enabled, and if it's not
 	   supposed to be only once and we have already said it, say it */
-    if ((avgholdmins+avgholdsecs) > 0 && qe->parent->announceholdtime &&
-        ((qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE && !qe->last_pos) ||
-        !(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE))) {
+	if ((avgholdmins+avgholdsecs) > 0 && qe->parent->announceholdtime &&
+		((qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE && !qe->last_pos) ||
+		!(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE))) {
 		res = play_file(qe->chan, qe->parent->sound_holdtime);
 		if (res) {
 			goto playout;
@@ -3899,6 +3909,7 @@ static void leave_queue(struct queue_ent *qe)
 			while ((pr_iter = AST_LIST_REMOVE_HEAD(&qe->qe_rules, list))) {
 				ast_free(pr_iter);
 			}
+			qe->pr = NULL;
 			snprintf(posstr, sizeof(posstr), "%d", qe->pos);
 			pbx_builtin_setvar_helper(qe->chan, "QUEUEPOSITION", posstr);
 		} else {
@@ -3942,6 +3953,7 @@ static void callattempt_free(struct callattempt *doomed)
 		ao2_ref(doomed->member, -1);
 	}
 	ast_party_connected_line_free(&doomed->connected);
+	ast_free(doomed->orig_chan_name);
 	ast_free(doomed);
 }
 
@@ -4258,6 +4270,7 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies
 	/* Inherit specially named variables from parent channel */
 	ast_channel_inherit_variables(qe->chan, tmp->chan);
 	ast_channel_datastore_inherit(qe->chan, tmp->chan);
+	ast_max_forwards_decrement(tmp->chan);
 
 	/* Presense of ADSI CPE on outgoing channel follows ours */
 	ast_channel_adsicpe_set(tmp->chan, ast_channel_adsicpe(qe->chan));
@@ -4272,6 +4285,9 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies
 		ast_channel_exten_set(tmp->chan, ast_channel_exten(qe->chan));
 	}
 
+	/* Save the original channel name to detect call pickup masquerading in. */
+	tmp->orig_chan_name = ast_strdup(ast_channel_name(tmp->chan));
+
 	ast_channel_unlock(tmp->chan);
 	ast_channel_unlock(qe->chan);
 
@@ -4551,6 +4567,34 @@ static void rna(int rnatime, struct queue_ent *qe, struct ast_channel *peer, cha
 	return;
 }
 
+/*!
+ * \internal
+ * \brief Update connected line on chan from peer.
+ * \since 13.6.0
+ *
+ * \param chan Channel to get connected line updated.
+ * \param peer Channel providing connected line information.
+ * \param is_caller Non-zero if chan is the calling channel.
+ *
+ * \return Nothing
+ */
+static void update_connected_line_from_peer(struct ast_channel *chan, struct ast_channel *peer, int is_caller)
+{
+	struct ast_party_connected_line connected_caller;
+
+	ast_party_connected_line_init(&connected_caller);
+
+	ast_channel_lock(peer);
+	ast_connected_line_copy_from_caller(&connected_caller, ast_channel_caller(peer));
+	ast_channel_unlock(peer);
+	connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
+	if (ast_channel_connected_line_sub(peer, chan, &connected_caller, 0)
+		&& ast_channel_connected_line_macro(peer, chan, &connected_caller, is_caller, 0)) {
+		ast_channel_update_connected_line(chan, &connected_caller, NULL);
+	}
+	ast_party_connected_line_free(&connected_caller);
+}
+
 #define AST_MAX_WATCHERS 256
 /*!
  * \brief Wait for a member to answer the call
@@ -4585,12 +4629,9 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
 #ifdef HAVE_EPOLL
 	struct callattempt *epollo;
 #endif
-	struct ast_party_connected_line connected_caller;
 	char *inchan_name;
 	struct timeval start_time_tv = ast_tvnow();
 
-	ast_party_connected_line_init(&connected_caller);
-
 	ast_channel_lock(qe->chan);
 	inchan_name = ast_strdupa(ast_channel_name(qe->chan));
 	ast_channel_unlock(qe->chan);
@@ -4668,22 +4709,21 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
 			if (o->stillgoing && (o->chan) &&  (ast_channel_state(o->chan) == AST_STATE_UP)) {
 				if (!peer) {
 					ast_verb(3, "%s answered %s\n", ochan_name, inchan_name);
-					if (!o->block_connected_update) {
+					if (o->orig_chan_name
+						&& strcmp(o->orig_chan_name, ochan_name)) {
+						/*
+						 * The channel name changed so we must generate COLP update.
+						 * Likely because a call pickup channel masqueraded in.
+						 */
+						update_connected_line_from_peer(in, o->chan, 1);
+					} else if (!o->block_connected_update) {
 						if (o->pending_connected_update) {
 							if (ast_channel_connected_line_sub(o->chan, in, &o->connected, 0) &&
 								ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) {
 								ast_channel_update_connected_line(in, &o->connected, NULL);
 							}
 						} else if (!o->dial_callerid_absent) {
-							ast_channel_lock(o->chan);
-							ast_connected_line_copy_from_caller(&connected_caller, ast_channel_caller(o->chan));
-							ast_channel_unlock(o->chan);
-							connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
-							if (ast_channel_connected_line_sub(o->chan, in, &connected_caller, 0) &&
-								ast_channel_connected_line_macro(o->chan, in, &connected_caller, 1, 0)) {
-								ast_channel_update_connected_line(in, &connected_caller, NULL);
-							}
-							ast_party_connected_line_free(&connected_caller);
+							update_connected_line_from_peer(in, o->chan, 1);
 						}
 					}
 					if (o->aoc_s_rate_list) {
@@ -4751,6 +4791,7 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
 						ast_channel_lock_both(o->chan, in);
 						ast_channel_inherit_variables(in, o->chan);
 						ast_channel_datastore_inherit(in, o->chan);
+						ast_max_forwards_decrement(o->chan);
 
 						if (o->pending_connected_update) {
 							/*
@@ -4763,6 +4804,9 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
 							ast_party_connected_line_copy(&o->connected, ast_channel_connected(in));
 						}
 
+						ast_free(o->orig_chan_name);
+						o->orig_chan_name = ast_strdup(ast_channel_name(o->chan));
+
 						ast_channel_req_accountcodes(o->chan, in, AST_CHANNEL_REQUESTOR_BRIDGE_PEER);
 
 						if (!ast_channel_redirecting(o->chan)->from.number.valid
@@ -4838,22 +4882,21 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
 								ast_verb(3, "%s answered %s\n", ochan_name, inchan_name);
 								ast_channel_publish_dial(qe->chan, o->chan, on, "ANSWER");
 								publish_dial_end_event(qe->chan, outgoing, o->chan, "CANCEL");
-								if (!o->block_connected_update) {
+								if (o->orig_chan_name
+									&& strcmp(o->orig_chan_name, ochan_name)) {
+									/*
+									 * The channel name changed so we must generate COLP update.
+									 * Likely because a call pickup channel masqueraded in.
+									 */
+									update_connected_line_from_peer(in, o->chan, 1);
+								} else if (!o->block_connected_update) {
 									if (o->pending_connected_update) {
 										if (ast_channel_connected_line_sub(o->chan, in, &o->connected, 0) &&
 											ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) {
 											ast_channel_update_connected_line(in, &o->connected, NULL);
 										}
 									} else if (!o->dial_callerid_absent) {
-										ast_channel_lock(o->chan);
-										ast_connected_line_copy_from_caller(&connected_caller, ast_channel_caller(o->chan));
-										ast_channel_unlock(o->chan);
-										connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
-										if (ast_channel_connected_line_sub(o->chan, in, &connected_caller, 0) &&
-											ast_channel_connected_line_macro(o->chan, in, &connected_caller, 1, 0)) {
-											ast_channel_update_connected_line(in, &connected_caller, NULL);
-										}
-										ast_party_connected_line_free(&connected_caller);
+										update_connected_line_from_peer(in, o->chan, 1);
 									}
 								}
 								if (o->aoc_s_rate_list) {
@@ -5243,7 +5286,7 @@ static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *r
 
 			if ((status = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty, qe->parent->leavewhenempty, 0))) {
 				*reason = QUEUE_LEAVEEMPTY;
-				ast_queue_log(qe->parent->name, ast_channel_uniqueid(qe->chan), "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
+				ast_queue_log(qe->parent->name, ast_channel_uniqueid(qe->chan), "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) (time(NULL) - qe->start));
 				leave_queue(qe);
 				break;
 			}
@@ -5553,6 +5596,10 @@ struct queue_stasis_data {
 	struct local_optimization caller_optimize;
 	/*! Local channel optimization details for the member */
 	struct local_optimization member_optimize;
+	/*! Member channel */
+	struct ast_channel *member_channel;
+	/*! Caller channel */
+	struct ast_channel *caller_channel;
 };
 
 /*!
@@ -5570,6 +5617,9 @@ static void queue_stasis_data_destructor(void *obj)
 	ao2_cleanup(queue_data->member);
 	queue_unref(queue_data->queue);
 	ast_string_field_free_memory(queue_data);
+
+	ao2_ref(queue_data->member_channel, -1);
+	ao2_ref(queue_data->caller_channel, -1);
 }
 
 /*!
@@ -5616,6 +5666,16 @@ static struct queue_stasis_data *queue_stasis_data_alloc(struct queue_ent *qe,
 	queue_data->caller_pos = qe->opos;
 	ao2_ref(mem, +1);
 	queue_data->member = mem;
+
+	/*
+	 * During transfers it's possible for both the member and/or caller
+	 * channel(s) to not be available. Adding a reference here ensures
+	 * that the channels remain until app_queue is completely done with
+	 * them.
+	 */
+	queue_data->member_channel = ao2_bump(peer);
+	queue_data->caller_channel = ao2_bump(qe->chan);
+
 	return queue_data;
 }
 
@@ -5661,8 +5721,8 @@ static void log_attended_transfer(struct queue_stasis_data *queue_data, struct a
 
 	ast_queue_log(queue_data->queue->name, caller->uniqueid, queue_data->member->membername, "ATTENDEDTRANSFER", "%s|%ld|%ld|%d",
 			ast_str_buffer(transfer_str),
-			(long) queue_data->starttime - queue_data->holdstart,
-			(long) time(NULL) - queue_data->starttime, queue_data->caller_pos);
+			(long) (queue_data->starttime - queue_data->holdstart),
+			(long) (time(NULL) - queue_data->starttime), queue_data->caller_pos);
 }
 
 /*!
@@ -5748,8 +5808,8 @@ static void handle_blind_transfer(void *userdata, struct stasis_subscription *su
 	ast_queue_log(queue_data->queue->name, caller_snapshot->uniqueid, queue_data->member->membername,
 			"BLINDTRANSFER", "%s|%s|%ld|%ld|%d",
 			exten, context,
-			(long) queue_data->starttime - queue_data->holdstart,
-			(long) time(NULL) - queue_data->starttime, queue_data->caller_pos);
+			(long) (queue_data->starttime - queue_data->holdstart),
+			(long) (time(NULL) - queue_data->starttime), queue_data->caller_pos);
 
 	send_agent_complete(queue_data->queue->name, caller_snapshot, member_snapshot, queue_data->member,
 			queue_data->holdstart, queue_data->starttime, TRANSFER);
@@ -5987,7 +6047,9 @@ static void handle_hangup(void *userdata, struct stasis_subscription *sub,
 	}
 
 	chan = ast_channel_get_by_name(channel_blob->snapshot->name);
-	if (chan && ast_channel_has_role(chan, AST_TRANSFERER_ROLE_NAME)) {
+	if (chan && (ast_channel_has_role(chan, AST_TRANSFERER_ROLE_NAME) ||
+		     !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "ATTENDEDTRANSFER")) ||
+		     !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "BLINDTRANSFER")))) {
 		/* Channel that is hanging up is doing it as part of a transfer.
 		 * We'll get a transfer event later
 		 */
@@ -6232,10 +6294,7 @@ static void setup_mixmonitor(struct queue_ent *qe, const char *filename)
  *
  * Here is the process of this function
  * 1. Process any options passed to the Queue() application. Options here mean the third argument to Queue()
- * 2. Iterate trough the members of the queue, creating a callattempt corresponding to each member. During this
- *    iteration, we also check the dialed_interfaces datastore to see if we have already attempted calling this
- *    member. If we have, we do not create a callattempt. This is in place to prevent call forwarding loops. Also
- *    during each iteration, we call calc_metric to determine which members should be rung when.
+ * 2. Iterate trough the members of the queue, creating a callattempt corresponding to each member.
  * 3. Call ring_one to place a call to the appropriate member(s)
  * 4. Call wait_for_answer to wait for an answer. If no one answers, return.
  * 5. Take care of any holdtime announcements, member delays, or other options which occur after a call has been answered.
@@ -6288,13 +6347,8 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
 	int block_connected_line = 0;
 	int callcompletedinsl;
 	struct ao2_iterator memi;
-	struct ast_datastore *datastore;
 	struct queue_end_bridge *queue_end_bridge = NULL;
 
-	ast_channel_lock(qe->chan);
-	datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
-	ast_channel_unlock(qe->chan);
-
 	memset(&bridge_config, 0, sizeof(bridge_config));
 	tmpid[0] = 0;
 	time(&now);
@@ -6381,73 +6435,12 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
 	memi = ao2_iterator_init(qe->parent->members, 0);
 	while ((cur = ao2_iterator_next(&memi))) {
 		struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
-		struct ast_dialed_interface *di;
-		AST_LIST_HEAD(,ast_dialed_interface) *dialed_interfaces;
 		if (!tmp) {
 			ao2_ref(cur, -1);
 			ao2_iterator_destroy(&memi);
 			ao2_unlock(qe->parent);
 			goto out;
 		}
-		if (!datastore) {
-			if (!(datastore = ast_datastore_alloc(&dialed_interface_info, NULL))) {
-				callattempt_free(tmp);
-				ao2_ref(cur, -1);
-				ao2_iterator_destroy(&memi);
-				ao2_unlock(qe->parent);
-				goto out;
-			}
-			datastore->inheritance = DATASTORE_INHERIT_FOREVER;
-			if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
-				callattempt_free(tmp);
-				ao2_ref(cur, -1);
-				ao2_iterator_destroy(&memi);
-				ao2_unlock(qe->parent);
-				goto out;
-			}
-			datastore->data = dialed_interfaces;
-			AST_LIST_HEAD_INIT(dialed_interfaces);
-
-			ast_channel_lock(qe->chan);
-			ast_channel_datastore_add(qe->chan, datastore);
-			ast_channel_unlock(qe->chan);
-		} else
-			dialed_interfaces = datastore->data;
-
-		AST_LIST_LOCK(dialed_interfaces);
-		AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
-			if (!strcasecmp(cur->interface, di->interface)) {
-				ast_debug(1, "Skipping dialing interface '%s' since it has already been dialed\n",
-					di->interface);
-				break;
-			}
-		}
-		AST_LIST_UNLOCK(dialed_interfaces);
-
-		if (di) {
-			callattempt_free(tmp);
-			ao2_ref(cur, -1);
-			continue;
-		}
-
-		/* It is always ok to dial a Local interface.  We only keep track of
-		 * which "real" interfaces have been dialed.  The Local channel will
-		 * inherit this list so that if it ends up dialing a real interface,
-		 * it won't call one that has already been called. */
-		if (strncasecmp(cur->interface, "Local/", 6)) {
-			if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) {
-				callattempt_free(tmp);
-				ao2_ref(cur, -1);
-				ao2_iterator_destroy(&memi);
-				ao2_unlock(qe->parent);
-				goto out;
-			}
-			strcpy(di->interface, cur->interface);
-
-			AST_LIST_LOCK(dialed_interfaces);
-			AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
-			AST_LIST_UNLOCK(dialed_interfaces);
-		}
 
 		/*
 		 * Seed the callattempt's connected line information with previously
@@ -6506,17 +6499,7 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
 	lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies,
 		ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT),
 		forwardsallowed, ringing);
-	/* The ast_channel_datastore_remove() function could fail here if the
-	 * datastore was moved to another channel during a masquerade. If this is
-	 * the case, don't free the datastore here because later, when the channel
-	 * to which the datastore was moved hangs up, it will attempt to free this
-	 * datastore again, causing a crash
-	 */
-	ast_channel_lock(qe->chan);
-	if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) {
-		ast_datastore_free(datastore);
-	}
-	ast_channel_unlock(qe->chan);
+
 	ao2_lock(qe->parent);
 	if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_RRORDERED) {
 		store_next_rr(qe, outgoing);
@@ -6577,11 +6560,11 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
 				}
 				if (!res2 && qe->parent->reportholdtime) {
 					if (!play_file(peer, qe->parent->sound_reporthold)) {
-						int holdtime, holdtimesecs;
+						long holdtime, holdtimesecs;
 
 						time(&now);
-						holdtime = abs((now - qe->start) / 60);
-						holdtimesecs = abs((now - qe->start) % 60);
+						holdtime = labs((now - qe->start) / 60);
+						holdtimesecs = labs((now - qe->start) % 60);
 						if (holdtime > 0) {
 							ast_say_number(peer, holdtime, AST_DIGIT_ANY, ast_channel_language(peer), NULL);
 							if (play_file(peer, qe->parent->sound_minutes) < 0) {
@@ -6616,7 +6599,7 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
 			} else if (ast_check_hangup(qe->chan)) {
 				/* Caller must have hung up just before being connected */
 				ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", ast_channel_name(peer));
-				ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
+				ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long) (time(NULL) - qe->start));
 				record_abandoned(qe);
 				ast_channel_publish_dial(qe->chan, peer, member->interface, ast_hangup_cause_to_dial_status(ast_channel_hangupcause(peer)));
 				ast_autoservice_chan_hangup_peer(qe->chan, peer);
@@ -6664,7 +6647,7 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
 		/* 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",
-				(long) time(NULL) - qe->start, qe->opos);
+				(long) (time(NULL) - qe->start), qe->opos);
 			pbx_builtin_setvar_multiple(qe->chan, interfacevar);
 			pbx_builtin_setvar_multiple(peer, interfacevar);
 		}
@@ -6683,7 +6666,7 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
 		ast_channel_unlock(qe->chan);
 
 		/* Begin Monitoring */
-		if (qe->parent->monfmt && *qe->parent->monfmt) {
+		if (*qe->parent->monfmt) {
 			if (!qe->parent->montype) {
 				const char *monexec;
 				ast_debug(1, "Starting Monitor as requested.\n");
@@ -6790,14 +6773,14 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
 			}
 		}
 		qe->handled++;
-		ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "CONNECT", "%ld|%s|%ld", (long) time(NULL) - qe->start, ast_channel_uniqueid(peer),
+		ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "CONNECT", "%ld|%s|%ld", (long) (time(NULL) - qe->start), ast_channel_uniqueid(peer),
 													(long)(orig - to > 0 ? (orig - to) / 1000 : 0));
 
 		blob = ast_json_pack("{s: s, s: s, s: s, s: i, s: i}",
 				     "Queue", queuename,
 				     "Interface", member->interface,
 				     "MemberName", member->membername,
-				     "HoldTime", (long) time(NULL) - qe->start,
+				     "HoldTime", (long) (time(NULL) - qe->start),
 				     "RingTime", (long)(orig - to > 0 ? (orig - to) / 1000 : 0));
 		queue_publish_multi_channel_blob(qe->chan, peer, queue_agent_connect_type(), blob);
 
@@ -7039,6 +7022,55 @@ static int publish_queue_member_pause(struct call_queue *q, struct member *membe
 	return 0;
 }
 
+/*!
+ * \internal
+ * \brief Set the pause status of the specific queue member.
+ *
+ * \param q Which queue the member belongs.
+ * \param mem Queue member being paused/unpaused.
+ * \param reason Why is this happening (Can be NULL/empty for no reason given.)
+ * \param paused Set to 1 if the member is being paused or 0 to unpause.
+ *
+ * \pre The q is locked on entry.
+ *
+ * \return Nothing
+ */
+static void set_queue_member_pause(struct call_queue *q, struct member *mem, const char *reason, int paused)
+{
+	if (mem->paused == paused) {
+		ast_debug(1, "%spausing already-%spaused queue member %s:%s\n",
+			(paused ? "" : "un"), (paused ? "" : "un"), q->name, mem->interface);
+	}
+
+	if (mem->realtime) {
+		if (update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0")) {
+			ast_log(LOG_WARNING, "Failed %spause update of realtime queue member %s:%s\n",
+				(paused ? "" : "un"), q->name, mem->interface);
+		}
+	}
+
+	mem->paused = paused;
+	ast_devstate_changed(mem->paused ? QUEUE_PAUSED_DEVSTATE : QUEUE_UNPAUSED_DEVSTATE,
+		AST_DEVSTATE_CACHABLE, "Queue:%s_pause_%s", q->name, mem->interface);
+
+	if (queue_persistent_members) {
+		dump_queue_members(q);
+	}
+
+	if (is_member_available(q, mem)) {
+		ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE,
+			"Queue:%s_avail", q->name);
+	} else if (!num_available_members(q)) {
+		ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE,
+			"Queue:%s_avail", q->name);
+	}
+
+	ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"),
+		"%s", S_OR(reason, ""));
+
+	publish_queue_member_pause(q, mem, reason);
+}
+
 static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused)
 {
 	int found = 0;
@@ -7052,55 +7084,30 @@ static int set_member_paused(const char *queuename, const char *interface, const
 			struct member *mem;
 
 			if ((mem = interface_exists(q, interface))) {
-				if (mem->paused == paused) {
-					ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
-				}
-
-				if (mem->realtime) {
-					if (update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0")) {
-						ast_log(LOG_WARNING, "Failed %spausing realtime queue member %s:%s\n", (paused ? "" : "un"), q->name, interface);
-						ao2_ref(mem, -1);
-						ao2_unlock(q);
-						queue_t_unref(q, "Done with iterator");
-						continue;
-					}
-				}
-
-				mem->paused = paused;
-				ast_devstate_changed(mem->paused ? QUEUE_PAUSED_DEVSTATE : QUEUE_UNPAUSED_DEVSTATE,
-					AST_DEVSTATE_CACHABLE, "Queue:%s_pause_%s", q->name, mem->interface);
-				found++;
-
-				/* Before we do the PAUSE/UNPAUSE log, if this was a PAUSEALL/UNPAUSEALL, log that here, but only on the first found entry. */
-				if (found == 1) {
-
-					/* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
-					if (ast_strlen_zero(queuename)) {
-						ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
-					}
-				}
-
-				if (queue_persistent_members) {
-					dump_queue_members(q);
-				}
-
-				if (is_member_available(q, mem)) {
-					ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Queue:%s_avail", q->name);
-				} else if (!num_available_members(q)) {
-					ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, "Queue:%s_avail", q->name);
+				/*
+				 * Before we do the PAUSE/UNPAUSE, log if this was a
+				 * PAUSEALL/UNPAUSEALL but only on the first found entry.
+				 */
+				++found;
+				if (found == 1
+					&& ast_strlen_zero(queuename)) {
+					/*
+					 * XXX In all other cases, we use the queue name,
+					 * but since this affects all queues, we cannot.
+					 */
+					ast_queue_log("NONE", "NONE", mem->membername,
+						(paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
 				}
 
-				ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason, ""));
-
-				publish_queue_member_pause(q, mem, reason);
+				set_queue_member_pause(q, mem, reason, paused);
 				ao2_ref(mem, -1);
 			}
-		}
 
-		if (!ast_strlen_zero(queuename) && !strcasecmp(queuename, q->name)) {
-			ao2_unlock(q);
-			queue_t_unref(q, "Done with iterator");
-			break;
+			if (!ast_strlen_zero(queuename)) {
+				ao2_unlock(q);
+				queue_t_unref(q, "Done with iterator");
+				break;
+			}
 		}
 
 		ao2_unlock(q);
@@ -7146,6 +7153,31 @@ static int set_member_penalty_help_members(struct call_queue *q, const char *int
 	return foundinterface;
 }
 
+/*!
+ * \internal
+ * \brief Set the ringinuse value of the specific queue member.
+ *
+ * \param q Which queue the member belongs.
+ * \param mem Queue member being set.
+ * \param ringinuse Set to 1 if the member is called when inuse.
+ *
+ * \pre The q is locked on entry.
+ *
+ * \return Nothing
+ */
+static void set_queue_member_ringinuse(struct call_queue *q, struct member *mem, int ringinuse)
+{
+	if (mem->realtime) {
+		update_realtime_member_field(mem, q->name, realtime_ringinuse_field,
+			ringinuse ? "1" : "0");
+	}
+
+	mem->ringinuse = ringinuse;
+
+	ast_queue_log(q->name, "NONE", mem->interface, "RINGINUSE", "%d", ringinuse);
+	queue_publish_member_blob(queue_member_ringinuse_type(), queue_member_blob_create(q, mem));
+}
+
 static int set_member_ringinuse_help_members(struct call_queue *q, const char *interface, int ringinuse)
 {
 	struct member *mem;
@@ -7154,17 +7186,7 @@ static int set_member_ringinuse_help_members(struct call_queue *q, const char *i
 	ao2_lock(q);
 	if ((mem = interface_exists(q, interface))) {
 		foundinterface++;
-		if (mem->realtime) {
-			char rtringinuse[80];
-
-			sprintf(rtringinuse, "%i", ringinuse);
-			update_realtime_member_field(mem, q->name, realtime_ringinuse_field, rtringinuse);
-		}
-
-		mem->ringinuse = ringinuse;
-
-		ast_queue_log(q->name, "NONE", interface, "RINGINUSE", "%d", ringinuse);
-		queue_publish_member_blob(queue_member_ringinuse_type(), queue_member_blob_create(q, mem));
+		set_queue_member_ringinuse(q, mem, ringinuse);
 		ao2_ref(mem, -1);
 	}
 	ao2_unlock(q);
@@ -7707,12 +7729,22 @@ static int queue_exec(struct ast_channel *chan, const char *data)
 	struct queue_ent qe = { 0 };
 	struct ast_flags opts = { 0, };
 	char *opt_args[OPT_ARG_ARRAY_SIZE];
+	int max_forwards;
 
 	if (ast_strlen_zero(data)) {
 		ast_log(LOG_WARNING, "Queue requires an argument: queuename[,options[,URL[,announceoverride[,timeout[,agi[,macro[,gosub[,rule[,position]]]]]]]]]\n");
 		return -1;
 	}
 
+	ast_channel_lock(chan);
+	max_forwards = ast_max_forwards_get(chan);
+	ast_channel_unlock(chan);
+
+	if (max_forwards <= 0) {
+		ast_log(LOG_WARNING, "Channel '%s' cannot enter queue. Max forwards exceeded\n", ast_channel_name(chan));
+		return -1;
+	}
+
 	parse = ast_strdupa(data);
 	AST_STANDARD_APP_ARGS(args, parse);
 
@@ -7857,7 +7889,7 @@ check_turns:
 			reason = QUEUE_TIMEOUT;
 			res = 0;
 			ast_queue_log(args.queuename, ast_channel_uniqueid(chan),"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld",
-				qe.pos, qe.opos, (long) time(NULL) - qe.start);
+				qe.pos, qe.opos, (long) (time(NULL) - qe.start));
 			break;
 		}
 
@@ -7881,7 +7913,8 @@ check_turns:
 			record_abandoned(&qe);
 			reason = QUEUE_TIMEOUT;
 			res = 0;
-			ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
+			ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "EXITWITHTIMEOUT",
+				"%d|%d|%ld", qe.pos, qe.opos, (long) (time(NULL) - qe.start));
 			break;
 		}
 
@@ -7910,7 +7943,8 @@ check_turns:
 		/* exit after 'timeout' cycle if 'n' option enabled */
 		if (noption && tries >= ao2_container_count(qe.parent->members)) {
 			ast_verb(3, "Exiting on time-out cycle\n");
-			ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
+			ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "EXITWITHTIMEOUT",
+				"%d|%d|%ld", qe.pos, qe.opos, (long) (time(NULL) - qe.start));
 			record_abandoned(&qe);
 			reason = QUEUE_TIMEOUT;
 			res = 0;
@@ -7923,7 +7957,7 @@ check_turns:
 			record_abandoned(&qe);
 			reason = QUEUE_TIMEOUT;
 			res = 0;
-			ast_queue_log(qe.parent->name, ast_channel_uniqueid(qe.chan),"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", qe.pos, qe.opos, (long) time(NULL) - qe.start);
+			ast_queue_log(qe.parent->name, ast_channel_uniqueid(qe.chan),"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", qe.pos, qe.opos, (long) (time(NULL) - qe.start));
 			break;
 		}
 
@@ -7952,7 +7986,7 @@ stop:
 				record_abandoned(&qe);
 				ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "ABANDON",
 					"%d|%d|%ld", qe.pos, qe.opos,
-					(long) time(NULL) - qe.start);
+					(long) (time(NULL) - qe.start));
 				res = -1;
 			} else if (qcontinue) {
 				reason = QUEUE_CONTINUE;
@@ -7960,7 +7994,7 @@ stop:
 			}
 		} else if (qe.valid_digits) {
 			ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "EXITWITHKEY",
-				"%s|%d|%d|%ld", qe.digits, qe.pos, qe.opos, (long) time(NULL) - qe.start);
+				"%s|%d|%d|%ld", qe.digits, qe.pos, qe.opos, (long) (time(NULL) - qe.start));
 		}
 	}
 
@@ -8063,12 +8097,29 @@ static int queue_function_exists(struct ast_channel *chan, const char *cmd, char
 	return 0;
 }
 
+static struct member *get_interface_helper(struct call_queue *q, const char *interface)
+{
+	struct member *m;
+
+	if (ast_strlen_zero(interface)) {
+		ast_log(LOG_ERROR, "QUEUE_MEMBER: Missing required interface argument.\n");
+		return NULL;
+	}
+
+	m = interface_exists(q, interface);
+	if (!m) {
+		ast_log(LOG_ERROR, "Queue member interface '%s' not in queue '%s'.\n",
+			interface, q->name);
+	}
+	return m;
+}
+
 /*!
  * \brief Get number either busy / free / ready or total members of a specific queue
  * \brief Get or set member properties penalty / paused / ringinuse
  * \retval number of members (busy / free / ready / total) or member info (penalty / paused / ringinuse)
  * \retval -1 on error
-*/
+ */
 static int queue_function_mem_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 {
 	int count = 0;
@@ -8085,14 +8136,18 @@ static int queue_function_mem_read(struct ast_channel *chan, const char *cmd, ch
 	buf[0] = '\0';
 
 	if (ast_strlen_zero(data)) {
-		ast_log(LOG_ERROR, "Missing required argument. %s(<queuename>,<option>[<interface>])\n", cmd);
+		ast_log(LOG_ERROR,
+			"Missing required argument. %s(<queuename>,<option>[,<interface>])\n",
+			cmd);
 		return -1;
 	}
 
 	AST_STANDARD_APP_ARGS(args, data);
 
-	if (args.argc < 2) {
-		ast_log(LOG_ERROR, "Missing required argument. %s(<queuename>,<option>[<interface>])\n", cmd);
+	if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.option)) {
+		ast_log(LOG_ERROR,
+			"Missing required argument. %s(<queuename>,<option>[,<interface>])\n",
+			cmd);
 		return -1;
 	}
 
@@ -8131,27 +8186,29 @@ static int queue_function_mem_read(struct ast_channel *chan, const char *cmd, ch
 				ao2_ref(m, -1);
 			}
 			ao2_iterator_destroy(&mem_iter);
-		} else if (!strcasecmp(args.option, "count") || ast_strlen_zero(args.option)) {
+		} else if (!strcasecmp(args.option, "count")) {
 			count = ao2_container_count(q->members);
-		} else if (!strcasecmp(args.option, "penalty") && !ast_strlen_zero(args.interface) &&
-			   ((m = interface_exists(q, args.interface)))) {
-			count = m->penalty;
-			ao2_ref(m, -1);
-		} else if (!strcasecmp(args.option, "paused") && !ast_strlen_zero(args.interface) &&
-			   ((m = interface_exists(q, args.interface)))) {
-			count = m->paused;
-			ao2_ref(m, -1);
-		} else if ( (!strcasecmp(args.option, "ignorebusy") || !strcasecmp(args.option, "ringinuse")) &&
-			   !ast_strlen_zero(args.interface) &&
-			   ((m = interface_exists(q, args.interface)))) {
-			count = m->ringinuse;
-			ao2_ref(m, -1);
-		} else if (!ast_strlen_zero(args.interface)) {
-			ast_log(LOG_ERROR, "Queue member interface %s not in queue %s\n",
-				args.interface, args.queuename);
+		} else if (!strcasecmp(args.option, "penalty")) {
+			m = get_interface_helper(q, args.interface);
+			if (m) {
+				count = m->penalty;
+				ao2_ref(m, -1);
+			}
+		} else if (!strcasecmp(args.option, "paused")) {
+			m = get_interface_helper(q, args.interface);
+			if (m) {
+				count = m->paused;
+				ao2_ref(m, -1);
+			}
+		} else if ((!strcasecmp(args.option, "ignorebusy") /* ignorebusy is legacy */
+			|| !strcasecmp(args.option, "ringinuse"))) {
+			m = get_interface_helper(q, args.interface);
+			if (m) {
+				count = m->ringinuse;
+				ao2_ref(m, -1);
+			}
 		} else {
-			ast_log(LOG_ERROR, "Unknown option %s provided to %s, valid values are: "
-				"logged, free, ready, count, penalty, paused, ringinuse\n", args.option, cmd);
+			ast_log(LOG_ERROR, "%s: Invalid option '%s' provided.\n", cmd, args.option);
 		}
 		ao2_unlock(q);
 		queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER()");
@@ -8168,9 +8225,6 @@ static int queue_function_mem_read(struct ast_channel *chan, const char *cmd, ch
 static int queue_function_mem_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
 {
 	int memvalue;
-	struct call_queue *q;
-	struct member *m;
-	char rtvalue[80];
 
 	AST_DECLARE_APP_ARGS(args,
 		AST_APP_ARG(queuename);
@@ -8179,65 +8233,48 @@ static int queue_function_mem_write(struct ast_channel *chan, const char *cmd, c
 	);
 
 	if (ast_strlen_zero(data)) {
-		ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER(<queuename>,<option>,<interface>)\n");
+		ast_log(LOG_ERROR,
+			"Missing required argument. %s([<queuename>],<option>,<interface>)\n",
+			cmd);
 		return -1;
 	}
 
 	AST_STANDARD_APP_ARGS(args, data);
 
-	if (args.argc < 3) {
-		ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
+	if (ast_strlen_zero(args.option)
+		|| ast_strlen_zero(args.interface)) {
+		ast_log(LOG_ERROR,
+			"Missing required argument. %s([<queuename>],<option>,<interface>)\n",
+			cmd);
 		return -1;
 	}
 
-	if (ast_strlen_zero(args.interface) && ast_strlen_zero(args.option)) {
-		ast_log (LOG_ERROR, "<interface> and <option> parameter's can't be null\n");
-		return -1;
-	}
+	/*
+	 * If queuename is empty then the option will be
+	 * set for the interface in all queues.
+	 */
 
 	memvalue = atoi(value);
 	if (!strcasecmp(args.option, "penalty")) {
-		/* if queuename = NULL then penalty will be set for interface in all the queues.*/
 		if (set_member_value(args.queuename, args.interface, MEMBER_PENALTY, memvalue)) {
-			ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n");
+			ast_log(LOG_ERROR, "Invalid interface, queue, or penalty\n");
 			return -1;
 		}
-	} else if ((q = find_load_queue_rt_friendly(args.queuename))) {
-		ao2_lock(q);
-		if ((m = interface_exists(q, args.interface))) {
-			sprintf(rtvalue, "%s",(memvalue <= 0) ? "0" : "1");
-			if (!strcasecmp(args.option, "paused")) {
-				if (m->realtime) {
-					update_realtime_member_field(m, q->name, args.option, rtvalue);
-				}
-				m->paused = (memvalue <= 0) ? 0 : 1;
-				ast_devstate_changed(m->paused ? QUEUE_PAUSED_DEVSTATE : QUEUE_UNPAUSED_DEVSTATE,
-					AST_DEVSTATE_CACHABLE, "Queue:%s_pause_%s", q->name, args.interface);
-
-			} else if ((!strcasecmp(args.option, "ignorebusy")) || (!strcasecmp(args.option, "ringinuse"))) {
-				if (m->realtime) {
-					update_realtime_member_field(m, q->name, args.option, rtvalue);
-				}
-
-				m->ringinuse = (memvalue <= 0) ? 0 : 1;
-			} else {
-				ast_log(LOG_ERROR, "Invalid option, only penalty , paused or ringinuse/ignorebusy are valid\n");
-				ao2_ref(m, -1);
-				ao2_unlock(q);
-				ao2_ref(q, -1);
-				return -1;
-			}
-			ao2_ref(m, -1);
-		} else {
-			ao2_unlock(q);
-			ao2_ref(q, -1);
-			ast_log(LOG_ERROR, "Invalid interface for queue\n");
+	} else if (!strcasecmp(args.option, "paused")) {
+		memvalue = (memvalue <= 0) ? 0 : 1;
+		if (set_member_paused(args.queuename, args.interface, NULL, memvalue)) {
+			ast_log(LOG_ERROR, "Invalid interface or queue\n");
 			return -1;
 		}
-		ao2_unlock(q);
-		ao2_ref(q, -1);
-        } else {
-		ast_log(LOG_ERROR, "Invalid queue\n");
+	} else if (!strcasecmp(args.option, "ignorebusy") /* ignorebusy is legacy */
+		|| !strcasecmp(args.option, "ringinuse")) {
+		memvalue = (memvalue <= 0) ? 0 : 1;
+		if (set_member_value(args.queuename, args.interface, MEMBER_RINGINUSE, memvalue)) {
+			ast_log(LOG_ERROR, "Invalid interface or queue\n");
+			return -1;
+		}
+	} else {
+		ast_log(LOG_ERROR, "%s: Invalid option '%s' provided.\n", cmd, args.option);
 		return -1;
 	}
 	return 0;
@@ -9087,7 +9124,7 @@ static char *__queues_show(struct mansession *s, int fd, int argc, const char *
 				ast_str_set(&out, 0, "      %s", mem->membername);
 				if (strcasecmp(mem->membername, mem->interface)) {
 					ast_str_append(&out, 0, " (%s", mem->interface);
-					if (mem->state_interface) {
+					if (!ast_strlen_zero(mem->state_interface)) {
 						ast_str_append(&out, 0, " from %s", mem->state_interface);
 					}
 					ast_str_append(&out, 0, ")");
@@ -9337,19 +9374,21 @@ static int manager_queues_summary(struct mansession *s, const struct message *m)
 	int qmemavail = 0;
 	int qchancount = 0;
 	int qlongestholdtime = 0;
+	int qsummaries = 0;
 	const char *id = astman_get_header(m, "ActionID");
 	const char *queuefilter = astman_get_header(m, "Queue");
-	char idText[256] = "";
+	char idText[256];
 	struct call_queue *q;
 	struct queue_ent *qe;
 	struct member *mem;
 	struct ao2_iterator queue_iter;
 	struct ao2_iterator mem_iter;
 
-	astman_send_ack(s, m, "Queue summary will follow");
+	astman_send_listack(s, m, "Queue summary will follow", "start");
 	time(&now);
+	idText[0] = '\0';
 	if (!ast_strlen_zero(id)) {
-		snprintf(idText, 256, "ActionID: %s\r\n", id);
+		snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
 	}
 	queue_iter = ao2_iterator_init(queues, 0);
 	while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
@@ -9392,15 +9431,15 @@ static int manager_queues_summary(struct mansession *s, const struct message *m)
 				"%s"
 				"\r\n",
 				q->name, qmemcount, qmemavail, qchancount, q->holdtime, q->talktime, qlongestholdtime, idText);
+			++qsummaries;
 		}
 		ao2_unlock(q);
 		queue_t_unref(q, "Done with iterator");
 	}
 	ao2_iterator_destroy(&queue_iter);
-	astman_append(s,
-		"Event: QueueSummaryComplete\r\n"
-		"%s"
-		"\r\n", idText);
+
+	astman_send_list_complete_start(s, m, "QueueSummaryComplete", qsummaries);
+	astman_send_list_complete_end(s);
 
 	return RESULT_SUCCESS;
 }
@@ -9410,10 +9449,11 @@ static int manager_queues_status(struct mansession *s, const struct message *m)
 {
 	time_t now;
 	int pos;
+	int q_items = 0;
 	const char *id = astman_get_header(m,"ActionID");
 	const char *queuefilter = astman_get_header(m,"Queue");
 	const char *memberfilter = astman_get_header(m,"Member");
-	char idText[256] = "";
+	char idText[256];
 	struct call_queue *q;
 	struct queue_ent *qe;
 	float sl = 0;
@@ -9421,8 +9461,9 @@ static int manager_queues_status(struct mansession *s, const struct message *m)
 	struct ao2_iterator queue_iter;
 	struct ao2_iterator mem_iter;
 
-	astman_send_ack(s, m, "Queue status will follow");
+	astman_send_listack(s, m, "Queue status will follow", "start");
 	time(&now);
+	idText[0] = '\0';
 	if (!ast_strlen_zero(id)) {
 		snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
 	}
@@ -9450,6 +9491,8 @@ static int manager_queues_status(struct mansession *s, const struct message *m)
 				"\r\n",
 				q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted,
 				q->callsabandoned, q->servicelevel, sl, q->weight, idText);
+			++q_items;
+
 			/* List Queue Members */
 			mem_iter = ao2_iterator_init(q->members, 0);
 			while ((mem = ao2_iterator_next(&mem_iter))) {
@@ -9469,10 +9512,12 @@ static int manager_queues_status(struct mansession *s, const struct message *m)
 						"\r\n",
 						q->name, mem->membername, mem->interface, mem->state_interface, mem->dynamic ? "dynamic" : "static",
 						mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
+					++q_items;
 				}
 				ao2_ref(mem, -1);
 			}
 			ao2_iterator_destroy(&mem_iter);
+
 			/* List Queue Entries */
 			pos = 1;
 			for (qe = q->head; qe; qe = qe->next) {
@@ -9494,6 +9539,7 @@ static int manager_queues_status(struct mansession *s, const struct message *m)
 					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);
+				++q_items;
 			}
 		}
 		ao2_unlock(q);
@@ -9501,10 +9547,8 @@ static int manager_queues_status(struct mansession *s, const struct message *m)
 	}
 	ao2_iterator_destroy(&queue_iter);
 
-	astman_append(s,
-		"Event: QueueStatusComplete\r\n"
-		"%s"
-		"\r\n",idText);
+	astman_send_list_complete_start(s, m, "QueueStatusComplete", q_items);
+	astman_send_list_complete_end(s);
 
 	return RESULT_SUCCESS;
 }
@@ -10339,15 +10383,6 @@ static char *handle_queue_reload(struct ast_cli_entry *e, int cmd, struct ast_cl
 	return CLI_SUCCESS;
 }
 
-static const char qpm_cmd_usage[] =
-"Usage: queue pause member <channel> in <queue> reason <reason>\n";
-
-static const char qum_cmd_usage[] =
-"Usage: queue unpause member <channel> in <queue> reason <reason>\n";
-
-static const char qsmp_cmd_usage[] =
-"Usage: queue set member penalty <channel> from <queue> <penalty>\n";
-
 static struct ast_cli_entry cli_queue[] = {
 	AST_CLI_DEFINE(queue_show, "Show status of a specified queue"),
 	AST_CLI_DEFINE(handle_queue_rule_show, "Show the rules defined in queuerules.conf"),
diff --git a/apps/app_read.c b/apps/app_read.c
index 3dd6cb5..d2a1de7 100644
--- a/apps/app_read.c
+++ b/apps/app_read.c
@@ -31,7 +31,7 @@
  
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 356042 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/file.h"
 #include "asterisk/pbx.h"
diff --git a/apps/app_readexten.c b/apps/app_readexten.c
index 2ca9a21..deaf83f 100644
--- a/apps/app_readexten.c
+++ b/apps/app_readexten.c
@@ -30,7 +30,7 @@
  
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 357542 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/file.h"
 #include "asterisk/pbx.h"
diff --git a/apps/app_record.c b/apps/app_record.c
index e79c607..31a54e6 100644
--- a/apps/app_record.c
+++ b/apps/app_record.c
@@ -31,7 +31,7 @@
  
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428655 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/file.h"
 #include "asterisk/pbx.h"
@@ -294,9 +294,10 @@ static int record_exec(struct ast_channel *chan, const char *data)
 			}
 			count++;
 		} while (ast_fileexists(tmp, ext, ast_channel_language(chan)) > 0);
-		pbx_builtin_setvar_helper(chan, "RECORDED_FILE", tmp);
 	} else
 		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) {
diff --git a/apps/app_saycounted.c b/apps/app_saycounted.c
index 2035e8c..4eddc08 100644
--- a/apps/app_saycounted.c
+++ b/apps/app_saycounted.c
@@ -114,7 +114,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/logger.h"
 #include "asterisk/module.h"
diff --git a/apps/app_sayunixtime.c b/apps/app_sayunixtime.c
index 0453d3b..3b4d2b5 100644
--- a/apps/app_sayunixtime.c
+++ b/apps/app_sayunixtime.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 402819 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/file.h"
 #include "asterisk/channel.h"
diff --git a/apps/app_senddtmf.c b/apps/app_senddtmf.c
index 942d61f..be70d7b 100644
--- a/apps/app_senddtmf.c
+++ b/apps/app_senddtmf.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 374030 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/pbx.h"
 #include "asterisk/module.h"
diff --git a/apps/app_sendtext.c b/apps/app_sendtext.c
index 807aa33..98b7b2f 100644
--- a/apps/app_sendtext.c
+++ b/apps/app_sendtext.c
@@ -33,7 +33,7 @@
  
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 356042 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/file.h"
 #include "asterisk/channel.h"
diff --git a/apps/app_setcallerid.c b/apps/app_setcallerid.c
index 761f8ca..1bd093c 100644
--- a/apps/app_setcallerid.c
+++ b/apps/app_setcallerid.c
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/lock.h"
 #include "asterisk/file.h"
diff --git a/apps/app_skel.c b/apps/app_skel.c
index cfc5505..337539e 100644
--- a/apps/app_skel.c
+++ b/apps/app_skel.c
@@ -45,7 +45,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <math.h> /* log10 */
 #include "asterisk/file.h"
diff --git a/apps/app_sms.c b/apps/app_sms.c
index 4c3d50a..385f479 100644
--- a/apps/app_sms.c
+++ b/apps/app_sms.c
@@ -40,7 +40,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <dirent.h>
 #include <ctype.h>
@@ -783,7 +783,7 @@ static void sms_log(sms_t * h, char status)
 		unsigned char n;
 
 		if (h->mr >= 0) {
-			snprintf(mrs, sizeof(mrs), "%02X", (unsigned)h->mr);
+			snprintf(mrs, sizeof(mrs), "%02hhX", (unsigned char)h->mr);
 		}
 		snprintf(line, sizeof(line), "%s %c%c%c%s %s %s %s ",
 			isodate(time(NULL), buf, sizeof(buf)),
@@ -1016,7 +1016,7 @@ static void sms_writefile(sms_t * h)
 		unsigned int p;
 		fprintf(o, "udh#");
 		for (p = 0; p < h->udhl; p++) {
-			fprintf(o, "%02X", (unsigned)h->udh[p]);
+			fprintf(o, "%02hhX", (unsigned char)h->udh[p]);
 		}
 		fprintf(o, "\n");
 	}
@@ -1049,7 +1049,7 @@ static void sms_writefile(sms_t * h)
 			if (p == h->udl) {              /* can write in ucs-1 hex */
 				fprintf(o, "ud#");
 				for (p = 0; p < h->udl; p++) {
-					fprintf(o, "%02X", (unsigned)h->ud[p]);
+					fprintf(o, "%02hhX", (unsigned char)h->ud[p]);
 				}
 				fprintf(o, "\n");
 			} else {                        /* write in UCS-2 */
@@ -1140,7 +1140,7 @@ static unsigned char sms_handleincoming (sms_t * h)
 				return 0xFF;		  /* duh! */
 			}
 		} else {
-			ast_log(LOG_WARNING, "Unknown message type %02X\n", (unsigned)h->imsg[2]);
+			ast_log(LOG_WARNING, "Unknown message type %02hhX\n", h->imsg[2]);
 			return 0xFF;
 		}
 	} else {                                /* client */
@@ -1163,7 +1163,7 @@ static unsigned char sms_handleincoming (sms_t * h)
 				return 0xFF;                /* duh! */
 			}
 		} else {
-			ast_log(LOG_WARNING, "Unknown message type %02X\n", (unsigned)h->imsg[2]);
+			ast_log(LOG_WARNING, "Unknown message type %02hhX\n", h->imsg[2]);
 			return 0xFF;
 		}
 	}
@@ -1245,7 +1245,7 @@ static char *sms_hexdump(unsigned char buf[], int size, char *s /* destination *
 	int f;
 
 	for (p = s, f = 0; f < size && f < MAX_DEBUG_LEN; f++, p += 3) {
-		sprintf(p, "%02X ", (unsigned)buf[f]);
+		sprintf(p, "%02hhX ", (unsigned char)buf[f]);
 	}
 	return(s);
 }
@@ -1483,7 +1483,7 @@ static void sms_debug (int dir, sms_t *h)
 	int n = (dir == DIR_RX) ? h->ibytep : msg[1] + 2;
 	int q = 0;
 	while (q < n && q < 30) {
-		sprintf(p, " %02X", (unsigned)msg[q++]);
+		sprintf(p, " %02hhX", msg[q++]);
 		p += 3;
 	}
 	if (q < n) {
diff --git a/apps/app_softhangup.c b/apps/app_softhangup.c
index 861ab8e..f3fc4c1 100644
--- a/apps/app_softhangup.c
+++ b/apps/app_softhangup.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 356042 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/file.h"
 #include "asterisk/channel.h"
diff --git a/apps/app_speech_utils.c b/apps/app_speech_utils.c
index 0b412dc..d946038 100644
--- a/apps/app_speech_utils.c
+++ b/apps/app_speech_utils.c
@@ -32,7 +32,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419688 $");
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
 
 #include "asterisk/file.h"
 #include "asterisk/channel.h"
diff --git a/apps/app_stack.c b/apps/app_stack.c
index 7396d03..253f413 100644
--- a/apps/app_stack.c
+++ b/apps/app_stack.c
@@ -32,7 +32,7 @@
 
 #include "asterisk.h"
  
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 422719 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/pbx.h"
 #include "asterisk/module.h"
diff --git a/apps/app_stasis.c b/apps/app_stasis.c
index 377b8d9..4f53aff 100644
--- a/apps/app_stasis.c
+++ b/apps/app_stasis.c
@@ -30,10 +30,11 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/app.h"
 #include "asterisk/module.h"
+#include "asterisk/pbx.h"
 #include "asterisk/stasis.h"
 #include "asterisk/stasis_app_impl.h"
 
@@ -50,9 +51,26 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
 			</parameter>
 		</syntax>
 		<description>
-			<para>
-				Invoke a Stasis application.
-			</para>
+			<para>Invoke a Stasis application.</para>
+			<para>This application will set the following channel variable upon
+			completion:</para>
+			<variablelist>
+				<variable name="STASISSTATUS">
+					<para>This indicates the status of the execution of the
+					Stasis application.</para>
+					<value name="SUCCESS">
+						The channel has exited Stasis without any failures in
+						Stasis.
+					</value>
+					<value name="FAILED">
+						A failure occurred when executing the Stasis
+						The app registry is not instantiated; The app
+						application. Some (not all) possible reasons for this:
+						requested is not registered; The app requested is not
+						active; Stasis couldn't send a start message.
+					</value>
+				</variable>
+			</variablelist>
 		</description>
 	</application>
  ***/
@@ -67,6 +85,7 @@ static const char *stasis = "Stasis";
 static int app_exec(struct ast_channel *chan, const char *data)
 {
 	char *parse = NULL;
+	int ret = -1;
 
 	AST_DECLARE_APP_ARGS(args,
 		AST_APP_ARG(app_name);
@@ -76,17 +95,28 @@ static int app_exec(struct ast_channel *chan, const char *data)
 	ast_assert(chan != NULL);
 	ast_assert(data != NULL);
 
+	pbx_builtin_setvar_helper(chan, "STASISSTATUS", "");
+
 	/* parse the arguments */
 	parse = ast_strdupa(data);
 	AST_STANDARD_APP_ARGS(args, parse);
 
 	if (args.argc < 1) {
 		ast_log(LOG_WARNING, "Stasis app_name argument missing\n");
-		return -1;
+	} else {
+		ret = stasis_app_exec(chan,
+		                      args.app_name,
+		                      args.argc - 1,
+		                      args.app_argv);
+	}
+
+	if (ret == -1) {
+	    pbx_builtin_setvar_helper(chan, "STASISSTATUS", "FAILED");
+	} else {
+	    pbx_builtin_setvar_helper(chan, "STASISSTATUS", "SUCCESS");
 	}
 
-	return stasis_app_exec(
-		chan, args.app_name, args.argc - 1, args.app_argv);
+	return ret;
 }
 
 static int load_module(void)
diff --git a/apps/app_system.c b/apps/app_system.c
index f40255a..7fe453d 100644
--- a/apps/app_system.c
+++ b/apps/app_system.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 369013 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/pbx.h"
 #include "asterisk/module.h"
diff --git a/apps/app_talkdetect.c b/apps/app_talkdetect.c
index bb35089..a021252 100644
--- a/apps/app_talkdetect.c
+++ b/apps/app_talkdetect.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/lock.h"
 #include "asterisk/file.h"
diff --git a/apps/app_test.c b/apps/app_test.c
index 8b030a8..410d686 100644
--- a/apps/app_test.c
+++ b/apps/app_test.c
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/stat.h>
 
diff --git a/apps/app_transfer.c b/apps/app_transfer.c
index 9f1feba..1b81082 100644
--- a/apps/app_transfer.c
+++ b/apps/app_transfer.c
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 405830 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/pbx.h"
 #include "asterisk/module.h"
diff --git a/apps/app_url.c b/apps/app_url.c
index 7eb4e3e..d5dd701 100644
--- a/apps/app_url.c
+++ b/apps/app_url.c
@@ -31,7 +31,7 @@
  
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/pbx.h"
 #include "asterisk/module.h"
diff --git a/apps/app_userevent.c b/apps/app_userevent.c
index 9d3a3c7..8f7219e 100644
--- a/apps/app_userevent.c
+++ b/apps/app_userevent.c
@@ -27,7 +27,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 414406 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/pbx.h"
 #include "asterisk/module.h"
diff --git a/apps/app_verbose.c b/apps/app_verbose.c
index 9cabf41..c6fc8d1 100644
--- a/apps/app_verbose.c
+++ b/apps/app_verbose.c
@@ -30,7 +30,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 413589 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/app.h"
diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c
index d073c74..19bd896 100644
--- a/apps/app_voicemail.c
+++ b/apps/app_voicemail.c
@@ -102,7 +102,7 @@
 #endif
 #endif
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428865 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/paths.h"	/* use ast_config_AST_SPOOL_DIR */
 #include <sys/time.h>
@@ -617,6 +617,7 @@ static AST_LIST_HEAD_STATIC(vmstates, vmstate);
 #define VM_MESSAGEWRAP   (1 << 17)  /*!< Wrap around from the last message to the first, and vice-versa */
 #define VM_FWDURGAUTO    (1 << 18)  /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
 #define ERROR_LOCK_PATH  -100
+#define ERROR_MAX_MSGS   -101
 #define OPERATOR_EXIT     300
 
 enum vm_box {
@@ -1098,6 +1099,8 @@ static void read_password_from_file(const char *secretfn, char *password, int pa
 static int write_password_to_file(const char *secretfn, const char *password);
 static const char *substitute_escapes(const char *value);
 static int message_range_and_existence_check(struct vm_state *vms, const char *msg_ids [], size_t num_msgs, int *msg_nums, struct ast_vm_user *vmu);
+static void notify_new_state(struct ast_vm_user *vmu);
+
 /*!
  * Place a message in the indicated folder
  *
@@ -2171,6 +2174,8 @@ static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_
 	char *attachment;
 	int i;
 	BODY *body;
+	int ret = 0;
+	int curr_mbox;
 
 	/* This function is only used for retrieval of IMAP greetings
 	 * regular messages are not retrieved this way, nor are greetings
@@ -2204,6 +2209,10 @@ static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_
 	*vms_p->introfn = '\0';
 
 	ast_mutex_lock(&vms_p->lock);
+
+	/* get the current mailbox so that we can point the mailstream back to it later */
+	curr_mbox = get_folder_by_name(vms_p->curbox);
+
 	if (init_mailstream(vms_p, GREETINGS_FOLDER) || !vms_p->mailstream) {
 		ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
 		ast_mutex_unlock(&vms_p->lock);
@@ -2218,21 +2227,28 @@ static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_
 			attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
 		} else {
 			ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
-			ast_mutex_unlock(&vms_p->lock);
-			return -1;
+			ret = -1;
+			break;
 		}
 		filename = strsep(&attachment, ".");
 		if (!strcmp(filename, file)) {
 			ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
 			vms_p->msgArray[vms_p->curmsg] = i + 1;
 			save_body(body, vms_p, "2", attachment, 0);
-			ast_mutex_unlock(&vms_p->lock);
-			return 0;
+			ret = 0;
+			break;
 		}
 	}
-	ast_mutex_unlock(&vms_p->lock);
 
-	return -1;
+	if (curr_mbox != -1) {
+		/* restore previous mbox stream */
+		if (init_mailstream(vms_p, curr_mbox) || !vms_p->mailstream) {
+			ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
+			ret = -1;
+		}
+	}
+	ast_mutex_unlock(&vms_p->lock);
+	return ret;
 }
 
 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
@@ -2246,12 +2262,13 @@ static int imap_retrieve_file(const char *dir, const int msgnum, const char *mai
 	FILE *text_file_ptr;
 	int res = 0;
 	struct ast_vm_user *vmu;
+	int curr_mbox;
 
 	if (!(vmu = find_user(NULL, context, mailbox))) {
 		ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
 		return -1;
 	}
-	
+
 	if (msgnum < 0) {
 		if (imapgreetings) {
 			res = imap_retrieve_greeting(dir, msgnum, vmu);
@@ -2278,6 +2295,19 @@ static int imap_retrieve_file(const char *dir, const int msgnum, const char *mai
 		goto exit;
 	}
 
+	/* Ensure we have the correct mailbox open and have a valid mailstream for it */
+	curr_mbox = get_folder_by_name(vms->curbox);
+	if (curr_mbox < 0) {
+		ast_debug(3, "Mailbox folder curbox not set, defaulting to Inbox\n");
+		curr_mbox = 0;
+	}
+	init_mailstream(vms, curr_mbox);
+	if (!vms->mailstream) {
+		ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmu->mailbox);
+		res = -1;
+		goto exit;
+	}
+
 	make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
 	snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
 
@@ -2428,7 +2458,7 @@ static int __messagecount(const char *context, const char *mailbox, const char *
 	/* We have to get the user before we can open the stream! */
 	vmu = find_user(&vmus, context, mailbox);
 	if (!vmu) {
-		ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
+		ast_log(AST_LOG_WARNING, "Couldn't find mailbox %s in context %s\n", mailbox, context);
 		return -1;
 	} else {
 		/* No IMAP account available */
@@ -2444,7 +2474,6 @@ static int __messagecount(const char *context, const char *mailbox, const char *
 		free_user(vmu);
 		return -1;
 	}
-	ast_assert(msgnum < vms->msg_array_max);
 
 	/* check if someone is accessing this box right now... */
 	vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1);
@@ -3332,7 +3361,7 @@ static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
 	if (taglen < 1)
 		return NULL;
 
-	if (!(start = strstr(header, tag)))
+	if (!(start = strcasestr(header, tag)))
 		return NULL;
 
 	/* Since we can be called multiple times we should clear our buffer */
@@ -3741,7 +3770,7 @@ 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 retreived.
+ * \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.
@@ -6373,6 +6402,7 @@ static int msg_create_from_file(struct ast_vm_recording_data *recdata)
 		}
 
 		STORE(dir, recipient->mailbox, recipient->context, msgnum, NULL, recipient, fmt, 0, vms, "", msg_id);
+		notify_new_state(recipient);
 	}
 
 	free_user(recipient);
@@ -7064,7 +7094,7 @@ static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg
 	} else {
 		if (x >= vmu->maxmsg) {
 			ast_unlock_path(ddir);
-			return -1;
+			return ERROR_MAX_MSGS;
 		}
 	}
 	make_file(sfn, sizeof(sfn), dir, msg);
@@ -8011,7 +8041,7 @@ static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu,
  *   - attempt to determine the context and and mailbox, and then invoke leave_message() function to record and store the message.
  *
  * When in the forward message mode (is_new_message == 0):
- *   - retreives the current message to be forwarded
+ *   - retrieves the current message to be forwarded
  *   - copies the original message to a temporary file, so updates to the envelope can be done.
  *   - determines the target mailbox and folders
  *   - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
@@ -8889,7 +8919,7 @@ static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
 		} else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
 			/* Move to old folder before deleting */
 			res = save_to_folder(vmu, vms, x, 1, NULL, 0);
-			if (res == ERROR_LOCK_PATH) {
+			if (res == ERROR_LOCK_PATH || res == ERROR_MAX_MSGS) {
 				/* If save failed do not delete the message */
 				ast_log(AST_LOG_WARNING, "Save failed.  Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
 				vms->deleted[x] = 0;
@@ -9070,7 +9100,7 @@ static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
 		if (!res) 
 			res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, ast_channel_language(chan), NULL);
 		if (!res) {
-			if ((vms->newmessages == 1)) {
+			if (vms->newmessages == 1) {
 				res = ast_play_and_wait(chan, "vm-INBOX");
 				if (!res)
 					res = ast_play_and_wait(chan, "vm-message");
@@ -9084,7 +9114,7 @@ static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
 		res = ast_play_and_wait(chan, "vm-youhave");
 		if (!res)
 			res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), NULL);
-		if ((vms->oldmessages == 1)){
+		if (vms->oldmessages == 1){
 			res = ast_play_and_wait(chan, "vm-Old");
 			if (!res)
 				res = ast_play_and_wait(chan, "vm-message");
@@ -9315,7 +9345,7 @@ static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
 			if ((vms->oldmessages || vms->newmessages) && !res) {
 				res = ast_play_and_wait(chan, "vm-and");
 			} else if (!res) {
-				if ((vms->urgentmessages == 1))
+				if (vms->urgentmessages == 1)
 					res = ast_play_and_wait(chan, "vm-message");
 				else
 					res = ast_play_and_wait(chan, "vm-messages");
@@ -9328,7 +9358,7 @@ static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
 			if (vms->oldmessages && !res)
 				res = ast_play_and_wait(chan, "vm-and");
 			else if (!res) {
-				if ((vms->newmessages == 1))
+				if (vms->newmessages == 1)
 					res = ast_play_and_wait(chan, "vm-message");
 				else
 					res = ast_play_and_wait(chan, "vm-messages");
@@ -9480,7 +9510,7 @@ static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
 	}
 
 	if (vms->newmessages) {
-		if ((vms->newmessages == 1)) {
+		if (vms->newmessages == 1) {
 			res = ast_play_and_wait(chan, "digits/ett");
 			res = res ? res : ast_play_and_wait(chan, "vm-nytt");
 			res = res ? res : ast_play_and_wait(chan, "vm-message");
@@ -9524,7 +9554,7 @@ static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
 	}
 
 	if (vms->newmessages) {
-		if ((vms->newmessages == 1)) {
+		if (vms->newmessages == 1) {
 			res = ast_play_and_wait(chan, "digits/1");
 			res = res ? res : ast_play_and_wait(chan, "vm-ny");
 			res = res ? res : ast_play_and_wait(chan, "vm-message");
@@ -9559,7 +9589,7 @@ static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
 	res = ast_play_and_wait(chan, "vm-youhave");
 	if (!res) {
 		if (vms->newmessages) {
-			if ((vms->newmessages == 1))
+			if (vms->newmessages == 1)
 				res = ast_play_and_wait(chan, "digits/1F");
 			else
 				res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
@@ -9568,7 +9598,7 @@ static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
 			if (vms->oldmessages && !res)
 				res = ast_play_and_wait(chan, "vm-and");
 			else if (!res) {
-				if ((vms->newmessages == 1))
+				if (vms->newmessages == 1)
 					res = ast_play_and_wait(chan, "vm-message");
 				else
 					res = ast_play_and_wait(chan, "vm-messages");
@@ -9615,7 +9645,7 @@ static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
 	if (!res) {
 		if (vms->newmessages) {
 			if (!res) {
-				if ((vms->newmessages == 1)) {
+				if (vms->newmessages == 1) {
 					res = ast_play_and_wait(chan, "digits/1M");
 					if (!res)
 						res = ast_play_and_wait(chan, "vm-message");
@@ -9666,7 +9696,7 @@ static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms) {
 	if (vms->newmessages) {
 		if (!res)
 			res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
-		if ((vms->newmessages == 1)) {
+		if (vms->newmessages == 1) {
 			if (!res)
 				res = ast_play_and_wait(chan, "vm-message");
 			if (!res)
@@ -9712,7 +9742,7 @@ static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
 			if (vms->oldmessages && !res)
 				res = ast_play_and_wait(chan, "vm-and");
 			else if (!res) {
-				if ((vms->newmessages == 1))
+				if (vms->newmessages == 1)
 					res = ast_play_and_wait(chan, "vm-message");
 				else
 					res = ast_play_and_wait(chan, "vm-messages");
@@ -9759,7 +9789,7 @@ static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
 			if (vms->oldmessages && !res)
 				res = ast_play_and_wait(chan, "vm-and");
 			else if (!res) {
-				if ((vms->newmessages == 1))
+				if (vms->newmessages == 1)
 					res = ast_play_and_wait(chan, "vm-message");
 				else
 					res = ast_play_and_wait(chan, "vm-messages");
@@ -9802,7 +9832,7 @@ static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
 		if (vms->newmessages) {
 			res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
 			if (!res) {
-				if ((vms->newmessages == 1)) {
+				if (vms->newmessages == 1) {
 					res = ast_play_and_wait(chan, "vm-message");
 					if (!res)
 						res = ast_play_and_wait(chan, "vm-INBOXs");
@@ -9868,7 +9898,7 @@ static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
 				res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
 			}
 			if (!res) {
-				if ((vms->newmessages == 1))
+				if (vms->newmessages == 1)
 					res = ast_play_and_wait(chan, "vm-novou");
 				if ((vms->newmessages) > 1 && (vms->newmessages < 5))
 					res = ast_play_and_wait(chan, "vm-nove");
@@ -9878,7 +9908,7 @@ static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
 			if (vms->oldmessages && !res)
 				res = ast_play_and_wait(chan, "vm-and");
 			else if (!res) {
-				if ((vms->newmessages == 1))
+				if (vms->newmessages == 1)
 					res = ast_play_and_wait(chan, "vm-zpravu");
 				if ((vms->newmessages) > 1 && (vms->newmessages < 5))
 					res = ast_play_and_wait(chan, "vm-zpravy");
@@ -9889,7 +9919,7 @@ static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
 		if (!res && vms->oldmessages) {
 			res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
 			if (!res) {
-				if ((vms->oldmessages == 1))
+				if (vms->oldmessages == 1)
 					res = ast_play_and_wait(chan, "vm-starou");
 				if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
 					res = ast_play_and_wait(chan, "vm-stare");
@@ -9897,7 +9927,7 @@ static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
 					res = ast_play_and_wait(chan, "vm-starych");
 			}
 			if (!res) {
-				if ((vms->oldmessages == 1))
+				if (vms->oldmessages == 1)
 					res = ast_play_and_wait(chan, "vm-zpravu");
 				if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
 					res = ast_play_and_wait(chan, "vm-zpravy");
@@ -13049,10 +13079,13 @@ static int manager_list_voicemail_users(struct mansession *s, const struct messa
 {
 	struct ast_vm_user *vmu = NULL;
 	const char *id = astman_get_header(m, "ActionID");
-	char actionid[128] = "";
+	char actionid[128];
+	int num_users = 0;
 
-	if (!ast_strlen_zero(id))
+	actionid[0] = '\0';
+	if (!ast_strlen_zero(id)) {
 		snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
+	}
 
 	AST_LIST_LOCK(&users);
 
@@ -13062,20 +13095,20 @@ static int manager_list_voicemail_users(struct mansession *s, const struct messa
 		return RESULT_SUCCESS;
 	}
 	
-	astman_send_ack(s, m, "Voicemail user list will follow");
+	astman_send_listack(s, m, "Voicemail user list will follow", "start");
 	
 	AST_LIST_TRAVERSE(&users, vmu, list) {
 		char dirname[256];
-
 #ifdef IMAP_STORAGE
 		int new, old;
+
 		inboxcount(vmu->mailbox, &new, &old);
 #endif
 		
 		make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
 		astman_append(s,
-			"%s"
 			"Event: VoicemailUserEntry\r\n"
+			"%s"
 			"VMContext: %s\r\n"
 			"VoiceMailbox: %s\r\n"
 			"Fullname: %s\r\n"
@@ -13144,8 +13177,11 @@ static int manager_list_voicemail_users(struct mansession *s, const struct messa
 			count_messages(vmu, dirname)
 #endif
 			);
+		++num_users;
 	}		
-	astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
+
+	astman_send_list_complete_start(s, m, "VoicemailUserEntryComplete", num_users);
+	astman_send_list_complete_end(s);
 
 	AST_LIST_UNLOCK(&users);
 
@@ -15176,6 +15212,7 @@ static int play_record_review(struct ast_channel *chan, char *playfile, char *re
 				} else {
 					ast_play_and_wait(chan, "vm-deleted");
 					DELETE(tempfile, -1, tempfile, vmu);
+					DISPOSE(tempfile, -1);
 					cmd = '0';
 				}
 			}
diff --git a/apps/app_waitforring.c b/apps/app_waitforring.c
index de4114a..7ed906b 100644
--- a/apps/app_waitforring.c
+++ b/apps/app_waitforring.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/file.h"
 #include "asterisk/channel.h"
diff --git a/apps/app_waitforsilence.c b/apps/app_waitforsilence.c
index e96be99..07f2824 100644
--- a/apps/app_waitforsilence.c
+++ b/apps/app_waitforsilence.c
@@ -44,7 +44,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/file.h"
 #include "asterisk/channel.h"
diff --git a/apps/app_waituntil.c b/apps/app_waituntil.c
index 49c29a0..d1f4bb1 100644
--- a/apps/app_waituntil.c
+++ b/apps/app_waituntil.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 389247 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/logger.h"
 #include "asterisk/channel.h"
diff --git a/apps/app_while.c b/apps/app_while.c
index 1c7e9ca..90d3d58 100644
--- a/apps/app_while.c
+++ b/apps/app_while.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 370655 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/pbx.h"
 #include "asterisk/module.h"
diff --git a/apps/app_zapateller.c b/apps/app_zapateller.c
index c8ca2cc..8e966e9 100644
--- a/apps/app_zapateller.c
+++ b/apps/app_zapateller.c
@@ -31,7 +31,7 @@
  
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/lock.h"
 #include "asterisk/file.h"
diff --git a/apps/confbridge/conf_chan_announce.c b/apps/confbridge/conf_chan_announce.c
index 2222624..6596a85 100644
--- a/apps/confbridge/conf_chan_announce.c
+++ b/apps/confbridge/conf_chan_announce.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 410672 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/channel.h"
 #include "asterisk/bridge.h"
diff --git a/apps/confbridge/conf_chan_record.c b/apps/confbridge/conf_chan_record.c
index 90c0966..54317e4 100644
--- a/apps/confbridge/conf_chan_record.c
+++ b/apps/confbridge/conf_chan_record.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419044 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/channel.h"
 #include "asterisk/bridge.h"
@@ -38,6 +38,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419044 $")
 
 /* ------------------------------------------------------------------- */
 
+static unsigned int name_sequence = 0;
+
 static int rec_call(struct ast_channel *chan, const char *addr, int timeout)
 {
 	/* Make sure anyone calling ast_call() for this channel driver is going to fail. */
@@ -59,6 +61,7 @@ static struct ast_channel *rec_request(const char *type, struct ast_format_cap *
 	struct ast_channel *chan;
 	const char *conf_name = data;
 	RAII_VAR(struct ast_format_cap *, capabilities, NULL, ao2_cleanup);
+	int generated_seqno = ast_atomic_fetchadd_int((int *) &name_sequence, +1);
 
 	capabilities = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
 	if (!capabilities) {
@@ -67,8 +70,8 @@ static struct ast_channel *rec_request(const char *type, struct ast_format_cap *
 	ast_format_cap_append_by_type(capabilities, AST_MEDIA_TYPE_UNKNOWN);
 
 	chan = ast_channel_alloc(1, AST_STATE_UP, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0,
-		"CBRec/conf-%s-uid-%d",
-		conf_name, (int) ast_random());
+		"CBRec/conf-%s-uid-%08x",
+		conf_name, (unsigned) generated_seqno);
 	if (!chan) {
 		return NULL;
 	}
diff --git a/apps/confbridge/conf_config_parser.c b/apps/confbridge/conf_config_parser.c
index 7b2bfbc..b8b1e2a 100644
--- a/apps/confbridge/conf_config_parser.c
+++ b/apps/confbridge/conf_config_parser.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 413589 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/logger.h"
 #include "asterisk/config.h"
 #include "asterisk/config_options.h"
@@ -227,6 +227,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 413589 $")
 				<configOption name="template">
 					<synopsis>When using the CONFBRIDGE dialplan function, use a user profile as a template for creating a new temporary profile</synopsis>
 				</configOption>
+				<configOption name="timeout">
+					<synopsis>Kick the user out of the conference after this many seconds. 0 means there is no timeout for the user.</synopsis>
+				</configOption>
 			</configObject>
 			<configObject name="bridge_profile">
 				<synopsis>A named profile to apply to specific bridges.</synopsis>
@@ -941,12 +944,17 @@ static const struct ast_datastore_info confbridge_datastore = {
 	.type = "confbridge",
 	.destroy = func_confbridge_destroy_cb
 };
+
 int func_confbridge_helper(struct ast_channel *chan, const char *cmd, char *data, const char *value)
 {
 	struct ast_datastore *datastore;
 	struct func_confbridge_data *b_data;
 	char *parse;
 	struct ast_variable tmpvar = { 0, };
+	struct ast_variable template = {
+		.name = "template",
+		.file = "CONFBRIDGE"
+	};
 	AST_DECLARE_APP_ARGS(args,
 		AST_APP_ARG(type);
 		AST_APP_ARG(option);
@@ -1019,7 +1027,14 @@ int func_confbridge_helper(struct ast_channel *chan, const char *cmd, char *data
 				ast_datastore_free(datastore);
 			}
 			return 0;
-		} else if (!aco_process_var(&bridge_type, "dialplan", &tmpvar, &b_data->b_profile)) {
+		}
+
+		if (b_data && !b_data->b_usable && strcasecmp(args.option, "template")) {
+			template.value = DEFAULT_BRIDGE_PROFILE;
+			aco_process_var(&bridge_type, "dialplan", &template, &b_data->b_profile);
+		}
+
+		if (!aco_process_var(&bridge_type, "dialplan", &tmpvar, &b_data->b_profile)) {
 			b_data->b_usable = 1;
 			return 0;
 		}
@@ -1029,7 +1044,14 @@ int func_confbridge_helper(struct ast_channel *chan, const char *cmd, char *data
 			user_profile_destructor(&b_data->u_profile);
 			memset(&b_data->u_profile, 0, sizeof(b_data->u_profile));
 			return 0;
-		} else if (!aco_process_var(&user_type, "dialplan", &tmpvar, &b_data->u_profile)) {
+		}
+
+		if (b_data && !b_data->u_usable && strcasecmp(args.option, "template")) {
+			template.value = DEFAULT_USER_PROFILE;
+			aco_process_var(&user_type, "dialplan", &template, &b_data->u_profile);
+		}
+
+		if (!aco_process_var(&user_type, "dialplan", &tmpvar, &b_data->u_profile)) {
 			b_data->u_usable = 1;
 			return 0;
 		}
@@ -1045,7 +1067,14 @@ int func_confbridge_helper(struct ast_channel *chan, const char *cmd, char *data
 				ast_datastore_free(datastore);
 			}
 			return 0;
-		} else if (!aco_process_var(&menu_type, "dialplan", &tmpvar, b_data->menu)) {
+		}
+
+		if (b_data && !b_data->m_usable && strcasecmp(args.option, "template")) {
+			template.value = DEFAULT_MENU_PROFILE;
+			aco_process_var(&menu_type, "dialplan", &template, &b_data->menu);
+		}
+
+		if (!aco_process_var(&menu_type, "dialplan", &tmpvar, b_data->menu)) {
 			b_data->m_usable = 1;
 			return 0;
 		}
@@ -2083,6 +2112,7 @@ int conf_load_config(void)
 	aco_option_register(&cfg_info, "dsp_silence_threshold", ACO_EXACT, user_types, __stringify(DEFAULT_SILENCE_THRESHOLD), OPT_UINT_T, 0, FLDSET(struct user_profile, silence_threshold));
 	aco_option_register(&cfg_info, "dsp_talking_threshold", ACO_EXACT, user_types, __stringify(DEFAULT_TALKING_THRESHOLD), OPT_UINT_T, 0, FLDSET(struct user_profile, talking_threshold));
 	aco_option_register(&cfg_info, "jitterbuffer", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_JITTERBUFFER);
+	aco_option_register(&cfg_info, "timeout", ACO_EXACT, user_types, "0", OPT_UINT_T, 0, FLDSET(struct user_profile, timeout));
 	/* This option should only be used with the CONFBRIDGE dialplan function */
 	aco_option_register_custom(&cfg_info, "template", ACO_EXACT, user_types, NULL, user_template_handler, 0);
 
@@ -2277,7 +2307,7 @@ int conf_find_menu_entry_by_sequence(const char *dtmf_sequence, struct conf_menu
 	return 0;
 }
 
-static int apply_menu_hooks(struct confbridge_user *user, struct conf_menu *menu)
+static int apply_menu_to_user(struct confbridge_user *user, struct conf_menu *menu)
 {
 	struct conf_menu_entry *menu_entry;
 
@@ -2301,6 +2331,7 @@ static int apply_menu_hooks(struct confbridge_user *user, struct conf_menu *menu
 			menu_hook_destroy(pvt);
 		}
 	}
+	strcpy(user->menu_name, menu->name); /* Safe */
 
 	return 0;
 }
@@ -2322,7 +2353,7 @@ int conf_set_menu_to_user(struct ast_channel *chan, struct confbridge_user *user
 			b_data = datastore->data;
 			if (b_data->m_usable) {
 				menu = ao2_bump(b_data->menu);
-				return apply_menu_hooks(user, menu);
+				return apply_menu_to_user(user, menu);
 			}
 		}
 	}
@@ -2340,7 +2371,7 @@ int conf_set_menu_to_user(struct ast_channel *chan, struct confbridge_user *user
 		return -1;
 	}
 
-	return apply_menu_hooks(user, menu);
+	return apply_menu_to_user(user, menu);
 }
 
 void conf_destroy_config(void)
diff --git a/apps/confbridge/confbridge_manager.c b/apps/confbridge/confbridge_manager.c
index cc8cfa3..eb1b58e 100644
--- a/apps/confbridge/confbridge_manager.c
+++ b/apps/confbridge/confbridge_manager.c
@@ -25,7 +25,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 422177 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/channel.h"
 #include "asterisk/bridge.h"
diff --git a/apps/confbridge/include/confbridge.h b/apps/confbridge/include/confbridge.h
index 4e155e6..8d2dffb 100644
--- a/apps/confbridge/include/confbridge.h
+++ b/apps/confbridge/include/confbridge.h
@@ -34,6 +34,8 @@
 #define MAX_CONF_NAME AST_MAX_EXTENSION
 /* Maximum length of a conference pin */
 #define MAX_PIN     80
+/* Maximum length of bridge/user/menu profile names */
+#define MAX_PROFILE_NAME 128
 
 #define DEFAULT_USER_PROFILE "default_user"
 #define DEFAULT_BRIDGE_PROFILE "default_bridge"
@@ -123,12 +125,12 @@ struct conf_menu_entry {
  * of DTMF sequences coupled with the actions those
  * sequences invoke.*/
 struct conf_menu {
-	char name[128];
+	char name[MAX_PROFILE_NAME];
 	AST_LIST_HEAD_NOLOCK(, conf_menu_entry) entries;
 };
 
 struct user_profile {
-	char name[128];
+	char name[MAX_PROFILE_NAME];
 	char pin[MAX_PIN];
 	char moh_class[128];
 	char announcement[PATH_MAX];
@@ -138,6 +140,8 @@ struct user_profile {
 	unsigned int talking_threshold;
 	/*! The time in ms of silence before a user is considered to be silent by the dsp. */
 	unsigned int silence_threshold;
+	/*! The time in ms the user may stay in the confbridge */
+	unsigned int timeout;
 };
 
 enum conf_sounds {
@@ -195,7 +199,7 @@ struct bridge_profile_sounds {
 };
 
 struct bridge_profile {
-	char name[64];
+	char name[MAX_PROFILE_NAME];
 	char language[MAX_LANGUAGE];		  /*!< Language used for playback_chan */
 	char rec_file[PATH_MAX];
 	unsigned int flags;
@@ -216,13 +220,11 @@ struct confbridge_conference {
 	unsigned int waitingusers;                                        /*!< Number of waiting users present */
 	unsigned int locked:1;                                            /*!< Is this conference bridge locked? */
 	unsigned int muted:1;                                             /*!< Is this conference bridge muted? */
-	unsigned int record_state:2;                                      /*!< Whether recording is started, stopped, or should exit */
 	struct ast_channel *playback_chan;                                /*!< Channel used for playback into the conference bridge */
 	struct ast_channel *record_chan;                                  /*!< Channel used for recording the conference */
-	pthread_t record_thread;                                          /*!< The thread the recording chan lives in */
+	struct ast_str *record_filename;                                  /*!< Recording filename. */
+	struct ast_str *orig_rec_file;                                    /*!< Previous b_profile.rec_file. */
 	ast_mutex_t playback_lock;                                        /*!< Lock used for playback channel */
-	ast_mutex_t record_lock;                                          /*!< Lock used for the record thread */
-	ast_cond_t record_cond;                                           /*!< Recording condition variable */
 	AST_LIST_HEAD_NOLOCK(, confbridge_user) active_list;              /*!< List of users participating in the conference bridge */
 	AST_LIST_HEAD_NOLOCK(, confbridge_user) waiting_list;             /*!< List of users waiting to join the conference bridge */
 };
@@ -239,7 +241,7 @@ struct confbridge_user {
 	struct confbridge_conference *conference;    /*!< Conference bridge they are participating in */
 	struct bridge_profile b_profile;             /*!< The Bridge Configuration Profile */
 	struct user_profile u_profile;               /*!< The User Configuration Profile */
-	char menu_name[64];                          /*!< The name of the DTMF menu assigned to this user */
+	char menu_name[MAX_PROFILE_NAME];            /*!< The name of the DTMF menu assigned to this user */
 	char name_rec_location[PATH_MAX];            /*!< Location of the User's name recorded file if it exists */
 	struct ast_channel *chan;                    /*!< Asterisk channel participating */
 	struct ast_bridge_features features;         /*!< Bridge features structure */
diff --git a/asterisk-13.1.1-summary.html b/asterisk-13.1.1-summary.html
deleted file mode 100644
index e1f0e59..0000000
--- a/asterisk-13.1.1-summary.html
+++ /dev/null
@@ -1,65 +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">
-<head><meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /><title>Release Summary - asterisk-13.1.1</title></head>
-<body>
-<h1 align="center"><a name="top">Release Summary</a></h1>
-<h3 align="center">asterisk-13.1.1</h3>
-<h3 align="center">Date: 2015-01-28</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="#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><br/><p>This release has been made to address one or more security vulnerabilities that have been identified.  A security advisory document has been published for each vulnerability that includes additional information.  Users of versions of Asterisk that are affected are strongly encouraged to review the advisories and determine what action they should take to protect their systems from these issues.</p>
-<p>Security Advisories: <a href="http://downloads.asterisk.org/pub/security/AST-2015-001.html">AST-2015-001</a>, <a href="http://downloads.asterisk.org/pub/security/AST-2015-002.html">AST-2015-002</a></p>
-<p>The data in this summary reflects changes that have been made since the previous release, asterisk-13.1.0.</p>
-<hr/>
-<a name="contributors"><h2 align="center">Contributors</h2></a>
-<center><a href="#top">[Back to Top]</a></center><br/><p>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 [...]
-<table width="100%" border="0">
-<tr>
-<td width="33%"><h3>Coders</h3></td>
-<td width="33%"><h3>Testers</h3></td>
-<td width="33%"><h3>Reporters</h3></td>
-</tr>
-<tr valign="top">
-<td>
-3 bebuild<br/>
-</td>
-<td>
-</td>
-<td>
-</td>
-</tr>
-</table>
-<hr/>
-<a name="commits"><h2 align="center">Commits Not Associated with an Issue</h2></a>
-<center><a href="#top">[Back to Top]</a></center><br/><p>This is a list of all changes that went into this release that did not directly close an issue from the issue tracker.  The commits may have been marked as being related to an issue.  If that is the case, the issue numbers are listed here, as well.</p>
-<table width="100%" border="1">
-<tr><td><b>Revision</b></td><td><b>Author</b></td><td><b>Summary</b></td><td><b>Issues Referenced</b></td></tr><tr><td><a href="http://svn.digium.com/view/asterisk/tags/13.1.1?view=revision&revision=431290">431290</a></td><td>bebuild</td><td>Create 13.1.1 tag</td>
-<td></td></tr><tr><td><a href="http://svn.digium.com/view/asterisk/tags/13.1.1?view=revision&revision=431296">431296</a></td><td>bebuild</td><td>Update .version; remove old summaries</td>
-<td></td></tr><tr><td><a href="http://svn.digium.com/view/asterisk/tags/13.1.1?view=revision&revision=431327">431327</a></td><td>bebuild</td><td>Merge r431301, 431303 for 13.1.1</td>
-<td></td></tr></table>
-<hr/>
-<a name="diffstat"><h2 align="center">Diffstat Results</h2></a>
-<center><a href="#top">[Back to Top]</a></center><br/><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>
-.version                     |    2
-ChangeLog                    |   35 +
-asterisk-13.1.0-summary.html |  747 ---------------------------------
-asterisk-13.1.0-summary.txt  |  947 -------------------------------------------
-funcs/func_curl.c            |   83 +++
-res/res_pjsip_sdp_rtp.c      |    1
-res/res_pjsip_session.c      |   60 ++
-res/res_pjsip_t38.c          |    1
-8 files changed, 173 insertions(+), 1703 deletions(-)
-</pre><br/>
-<hr/>
-</body>
-</html>
diff --git a/asterisk-13.1.1-summary.txt b/asterisk-13.1.1-summary.txt
deleted file mode 100644
index f48a6f5..0000000
--- a/asterisk-13.1.1-summary.txt
+++ /dev/null
@@ -1,96 +0,0 @@
-                                Release Summary
-
-                                asterisk-13.1.1
-
-                                Date: 2015-01-28
-
-                           <asteriskteam at digium.com>
-
-     ----------------------------------------------------------------------
-
-                               Table of Contents
-
-    1. Summary
-    2. Contributors
-    3. Other Changes
-    4. Diffstat
-
-     ----------------------------------------------------------------------
-
-                                    Summary
-
-                                 [Back to Top]
-
-   This release has been made to address one or more security vulnerabilities
-   that have been identified. A security advisory document has been published
-   for each vulnerability that includes additional information. Users of
-   versions of Asterisk that are affected are strongly encouraged to review
-   the advisories and determine what action they should take to protect their
-   systems from these issues.
-
-   Security Advisories: AST-2015-001, AST-2015-002
-
-   The data in this summary reflects changes that have been made since the
-   previous release, asterisk-13.1.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 closed by commits that went into this
-   release.
-
-     Coders                   Testers                  Reporters              
-   3 bebuild                
-
-     ----------------------------------------------------------------------
-
-                      Commits Not Associated with an Issue
-
-                                 [Back to Top]
-
-   This is a list of all changes that went into this release that did not
-   directly close an issue from the issue tracker. The commits may have been
-   marked as being related to an issue. If that is the case, the issue
-   numbers are listed here, as well.
-
-   +------------------------------------------------------------------------+
-   | Revision | Author  | Summary                       | Issues Referenced |
-   |----------+---------+-------------------------------+-------------------|
-   | 431290   | bebuild | Create 13.1.1 tag             |                   |
-   |----------+---------+-------------------------------+-------------------|
-   | 431296   | bebuild | Update .version; remove old   |                   |
-   |          |         | summaries                     |                   |
-   |----------+---------+-------------------------------+-------------------|
-   | 431327   | bebuild | Merge r431301, 431303 for     |                   |
-   |          |         | 13.1.1                        |                   |
-   +------------------------------------------------------------------------+
-
-     ----------------------------------------------------------------------
-
-                                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.
-
- .version                     |    2
- ChangeLog                    |   35 +
- asterisk-13.1.0-summary.html |  747 ---------------------------------
- asterisk-13.1.0-summary.txt  |  947 -------------------------------------------
- funcs/func_curl.c            |   83 +++
- res/res_pjsip_sdp_rtp.c      |    1
- res/res_pjsip_session.c      |   60 ++
- res/res_pjsip_t38.c          |    1
- 8 files changed, 173 insertions(+), 1703 deletions(-)
-
-     ----------------------------------------------------------------------
diff --git a/asterisk-13.7.2-summary.html b/asterisk-13.7.2-summary.html
new file mode 100644
index 0000000..39b3925
--- /dev/null
+++ b/asterisk-13.7.2-summary.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><title>Release Summary - asterisk-13.7.2</title><h1 align="center"><a name="top">Release Summary</a></h1><h3 align="center">asterisk-13.7.2</h3><h3 align="center">Date: 2016-02-05</h3><h3 align="center"><asteriskteam at digium.com></h3><hr><h2 align="center">Table of Contents</h2><ol>
+<li><a href="#summary">Summary</a></li>
+<li><a href="#contributors">Contributors</a></li>
+<li><a href="#closed_issues">Closed Issues</a></li>
+<li><a href="#commits">Other Changes</a></li>
+<li><a href="#diffstat">Diffstat</a></li>
+</ol><hr><a name="summary"><h2 align="center">Summary</h2></a><center><a href="#top">[Back to Top]</a></center><p>This release is a point release of an existing major version. The changes included were made to address problems that have been identified in this release series, or are minor, backwards compatible new features or improvements. Users should be able to safely upgrade to this version if this release series is already in use. Users considering upgrading from a previous version a [...]
+<tr><th width="33%">Coders</th><th width="33%">Testers</th><th width="33%">Reporters</th></tr>
+<tr valign="top"><td width="33%">3 Mark Michelson <mmichelson at lunkwill><br/>2 Mark Michelson <mmichelson at lunkwill.digium.internal><br/>1 Alexei Gradinari License #5691<br/></td><td width="33%"><td width="33%">1 Nic Colledge <nic at njcolledge.net><br/>1 Nic Colledge<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-25702">ASTERISK-25702</a>: PjSip realtime DB and Cache Errors since upgrade to asterisk-13.7.0 from asterisk-13.7.0-rc2<br/>Reported by: Nic Co [...]
+<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=8de94229baa3ee8471521c7c0c59424de0131c61">[8de94229ba]</a> Alexei Gradinari License #5691 -- res_sorcery_realtime: Fix regex regression.</li>
+</ul><br><h4>Category: Core/Sorcery</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25702">ASTERISK-25702</a>: PjSip realtime DB and Cache Errors since upgrade to asterisk-13.7.0 from asterisk-13.7.0-rc2<br/>Reported by: Nic Colledge<ul>
+<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=8de94229baa3ee8471521c7c0c59424de0131c61">[8de94229ba]</a> Alexei Gradinari License #5691 -- res_sorcery_realtime: Fix regex regression.</li>
+</ul><br><h4>Category: Resources/res_pjsip</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-25702">ASTERISK-25702</a>: PjSip realtime DB and Cache Errors since upgrade to asterisk-13.7.0 from asterisk-13.7.0-rc2<br/>Reported by: Nic Colledge<ul>
+<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=8de94229baa3ee8471521c7c0c59424de0131c61">[8de94229ba]</a> Alexei Gradinari License #5691 -- res_sorcery_realtime: Fix regex regression.</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=0ba44bd6f3b2db32272c43c70d6c265d3213a033">0ba44bd6f3</a></td><td>Mark Michelson</td><td>Release summaries: Remove previous versions</td></tr>
+<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=b750dfe202ce8153a7d56f99df9561a8ad9e97ea">b750dfe202</a></td><td>Mark Michelson</td><td>.version: Update for 13.7.2</td></tr>
+<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=c1b94ffe78e8240e374c2693449a6497eb0beb80">c1b94ffe78</a></td><td>Mark Michelson</td><td>.lastclean: Update for 13.7.2</td></tr>
+<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=932ed1ab5bcbecce3d19da579a5229c0308e87fa">932ed1ab5b</a></td><td>Mark Michelson</td><td>realtime: Add database scripts for 13.7.2</td></tr>
+<tr><td><a href="https://code.asterisk.org/code/changelog/asterisk?cs=a56f55d566023cd43bdfb80982e454e795878a1c">a56f55d566</a></td><td>Mark Michelson</td><td>Check for OpenSSL defines before trying to use them.</td></tr>
+</table><hr><a name="diffstat"><h2 align="center">Diffstat Results</h2></a><center><a href="#top">[Back to Top]</a></center><p>This is a summary of the changes to the source code that went into this release that was generated using the diffstat utility.</p><pre>asterisk-13.7.1-summary.html |   34 ---------
+asterisk-13.7.1-summary.txt  |  155 -------------------------------------------
+b/.version                   |    2
+b/configure                  |   89 ++++++++++++++++++++++++
+4 files changed, 90 insertions(+), 190 deletions(-)</pre><br></html>
\ No newline at end of file
diff --git a/asterisk-13.7.2-summary.txt b/asterisk-13.7.2-summary.txt
new file mode 100644
index 0000000..37069a9
--- /dev/null
+++ b/asterisk-13.7.2-summary.txt
@@ -0,0 +1,131 @@
+                                Release Summary
+
+                                asterisk-13.7.2
+
+                                Date: 2016-02-05
+
+                           <asteriskteam at digium.com>
+
+     ----------------------------------------------------------------------
+
+                               Table of Contents
+
+    1. Summary
+    2. Contributors
+    3. Closed Issues
+    4. Other Changes
+    5. Diffstat
+
+     ----------------------------------------------------------------------
+
+                                    Summary
+
+                                 [Back to Top]
+
+   This release is a point release of an existing major version. The changes
+   included were made to address problems that have been identified in this
+   release series, or are minor, backwards compatible new features or
+   improvements. Users should be able to safely upgrade to this version if
+   this release series is already in use. Users considering upgrading from a
+   previous version are strongly encouraged to review the UPGRADE.txt
+   document as well as the CHANGES document for information about upgrading
+   to this release series.
+
+   The data in this summary reflects changes that have been made since the
+   previous release, asterisk-13.7.1.
+
+     ----------------------------------------------------------------------
+
+                                  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               
+   3 Mark Michelson                                   1 Nic Colledge          
+   2 Mark Michelson                                   1 Nic Colledge          
+   1 Alexei Gradinari License #5691 
+
+     ----------------------------------------------------------------------
+
+                                 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-25702: PjSip realtime DB and Cache Errors since upgrade to
+   asterisk-13.7.0 from asterisk-13.7.0-rc2
+   Reported by: Nic Colledge
+     * [8de94229ba] Alexei Gradinari License #5691 -- res_sorcery_realtime:
+       Fix regex regression.
+
+    Category: Core/Sorcery
+
+   ASTERISK-25702: PjSip realtime DB and Cache Errors since upgrade to
+   asterisk-13.7.0 from asterisk-13.7.0-rc2
+   Reported by: Nic Colledge
+     * [8de94229ba] Alexei Gradinari License #5691 -- res_sorcery_realtime:
+       Fix regex regression.
+
+    Category: Resources/res_pjsip
+
+   ASTERISK-25702: PjSip realtime DB and Cache Errors since upgrade to
+   asterisk-13.7.0 from asterisk-13.7.0-rc2
+   Reported by: Nic Colledge
+     * [8de94229ba] Alexei Gradinari License #5691 -- res_sorcery_realtime:
+       Fix regex regression.
+
+     ----------------------------------------------------------------------
+
+                      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                                  |
+   |------------+----------------+------------------------------------------|
+   | 0ba44bd6f3 | Mark Michelson | Release summaries: Remove previous       |
+   |            |                | versions                                 |
+   |------------+----------------+------------------------------------------|
+   | b750dfe202 | Mark Michelson | .version: Update for 13.7.2              |
+   |------------+----------------+------------------------------------------|
+   | c1b94ffe78 | Mark Michelson | .lastclean: Update for 13.7.2            |
+   |------------+----------------+------------------------------------------|
+   | 932ed1ab5b | Mark Michelson | realtime: Add database scripts for       |
+   |            |                | 13.7.2                                   |
+   |------------+----------------+------------------------------------------|
+   | a56f55d566 | Mark Michelson | Check for OpenSSL defines before trying  |
+   |            |                | to use them.                             |
+   +------------------------------------------------------------------------+
+
+     ----------------------------------------------------------------------
+
+                                Diffstat Results
+
+                                 [Back to Top]
+
+   This is a summary of the changes to the source code that went into this
+   release that was generated using the diffstat utility.
+
+ asterisk-13.7.1-summary.html |   34 ---------
+ asterisk-13.7.1-summary.txt  |  155 -------------------------------------------
+ b/.version                   |    2
+ b/configure                  |   89 ++++++++++++++++++++++++
+ 4 files changed, 90 insertions(+), 190 deletions(-)
diff --git a/autoconf/ast_check_raii.m4 b/autoconf/ast_check_raii.m4
new file mode 100644
index 0000000..e39a43d
--- /dev/null
+++ b/autoconf/ast_check_raii.m4
@@ -0,0 +1,56 @@
+dnl check RAII requirements
+dnl
+dnl gcc / llvm-gcc: -fnested-functions
+dnl clang : -fblocks / -fblocks and -lBlocksRuntime"
+AC_DEFUN([AST_CHECK_RAII], [
+	AC_MSG_CHECKING([for RAII support])
+	AST_C_COMPILER_FAMILY=""
+	AC_LINK_IFELSE(
+		[AC_LANG_PROGRAM([], [
+			int main() {
+				#if defined(__clang__)
+				choke
+				#endif
+				return 0;
+			}
+			])
+		],[
+			dnl Nested functions required for RAII implementation
+			AC_MSG_CHECKING(for gcc -fnested-functions)
+			AC_COMPILE_IFELSE(
+				dnl Prototype needed due to http://gcc.gnu.org/bugzilla/show_bug.cgi?id=36774
+				[
+					AC_LANG_PROGRAM([], [auto void foo(void); void foo(void) {}])
+				],[
+					AST_NESTED_FUNCTIONS=""
+					AC_MSG_RESULT(no)
+				],[
+					AST_NESTED_FUNCTIONS="-fnested-functions"
+					AC_MSG_RESULT(yes)
+				]
+			)
+			AC_SUBST(AST_NESTED_FUNCTIONS)
+			AST_C_COMPILER_FAMILY="gcc"
+		],[
+			AC_MSG_CHECKING(for clang -fblocks)
+			if test "`echo "int main(){return ^{return 42;}();}" | ${CC} -o /dev/null -fblocks -x c - 2>&1`" = ""; then
+				AST_CLANG_BLOCKS_LIBS=""
+				AST_CLANG_BLOCKS="-Wno-unknown-warning-option -fblocks"
+				AC_MSG_RESULT(yes)
+			elif test "`echo "int main(){return ^{return 42;}();}" | ${CC} -o /dev/null -fblocks -x c -lBlocksRuntime - 2>&1`" = ""; then
+				AST_CLANG_BLOCKS_LIBS="-lBlocksRuntime"
+				AST_CLANG_BLOCKS="-fblocks"
+				AC_MSG_RESULT(yes)
+			else
+				AC_MSG_ERROR([BlocksRuntime is required for clang, please install libblocksruntime])
+			fi
+			AC_SUBST(AST_CLANG_BLOCKS_LIBS)
+			AC_SUBST(AST_CLANG_BLOCKS)
+			AST_C_COMPILER_FAMILY="clang"
+		]
+	)
+	if test -z "${AST_C_COMPILER_FAMILY}"; then
+		AC_MSG_ERROR([Compiler ${CC} not supported. Mminimum required gcc-4.3 / llvm-gcc-4.3 / clang-3.3 + libblocksruntime-dev])
+	fi
+	AC_SUBST(AST_C_COMPILER_FAMILY)
+])
diff --git a/autoconf/ast_check_strsep_array_bounds.m4 b/autoconf/ast_check_strsep_array_bounds.m4
new file mode 100644
index 0000000..47a41e5
--- /dev/null
+++ b/autoconf/ast_check_strsep_array_bounds.m4
@@ -0,0 +1,81 @@
+dnl macro AST_CHECK_STRSEP_ARRAY_BOUNDS0
+dnl
+dnl The optimized strcmp and strsep macro's in
+dnl /usr/include/xxx-linux-gnu/bits/string2.h produce a warning (-Warray-bounds)
+dnl when compiled with clang (+ -O1), when the delimiter parameter is
+dnl passed in as a char *, instead of the expected char[]
+dnl
+dnl Instead of replacing all occurrences of strsep and strcmp, looking like:
+dnl xxx_name = strsep(&rest, ",");
+dnl
+dnl with:
+dnl char delimiters[] = ",";
+dnl xxx_name = strsep(&rest, delimiters);
+dnl
+dnl to get around this warning, without having to suppress the warning completely.
+dnl This macro detects the warning and force these 'optimizations' to be
+dnl switched off (Clang already has a set of builtin optimizers which should result
+dnl in good performance for these type of functions).
+dnl
+dnl When the issue is detected it will add a define to autoconfig.h which will prevent
+dnl bits/string2.h from replacing the standard implementation of strsep/strcmp with it's
+dnl macro optimized version. bits/string.h checks these defines before inserting it's
+dnl replacements.
+dnl
+dnl When bits/string2.h get's fixed in the future, this macro should be able to
+dnl detect the new behaviour, and when no warning is generated, it will use the optimize
+dnl version from bits/string2.h
+dnl
+dnl
+dnl See 'define __strcmp_gc(s1, s2, l2) in bits/string2.h'
+dnl
+dnl llvm-comment: Normally, this array-bounds warning are suppressed for macros, so that
+dnl unused paths like the one that accesses __s1[3] are not warned about.  But if you
+dnl preprocess manually, and feed the result to another instance of clang, it will warn
+dnl about all the possible forks of this particular if statement.
+dnl
+dnl Instead of switching of this optimization, another solution would be to run the pre-
+dnl processing step with -frewrite-includes, which should preserve enough information
+dnl so that clang should still be able to suppress the diagnostic at the compile step
+dnl later on.
+dnl
+dnl See also "https://llvm.org/bugs/show_bug.cgi?id=20144"
+dnl See also "https://llvm.org/bugs/show_bug.cgi?id=11536"
+dnl
+AC_DEFUN([AST_CHECK_STRSEP_ARRAY_BOUNDS], [
+	AC_MSG_CHECKING([for clang strsep/strcmp optimization])
+	save_CFLAGS="$CFLAGS"
+	CFLAGS="$CFLAGS -O1 -Werror=array-bounds"
+	AC_COMPILE_IFELSE(
+		[
+		    	AC_LANG_SOURCE([
+				#include <stdio.h>
+				#include <string.h>
+
+				/* fails with clang and -O1 */
+				void test_strsep_strcmp (void) {
+					char *haystackstr = "test1,test2";
+					char *outstr;
+					if (!strcmp(haystackstr, ",")) {
+						printf("fail\n");
+					}
+					if ((outstr = strsep(&haystackstr, ","))) {
+						printf("fail:%s\n", outstr);
+					}
+				}
+				int main(int argc, char *argv[]) {
+					test_strsep_strcmp();
+					return 0;
+				}
+			])
+		],[
+			AC_MSG_RESULT(no)
+		],[
+			dnl setting this define in autoconfig.h will prevent bits/string2.h from replacing the standard implementation of strsep/strcmp
+			AC_DEFINE([_HAVE_STRING_ARCH_strcmp], 1, [Prevent clang array-bounds warning by not using strcmp from bits/string2.h])
+			AC_DEFINE([_HAVE_STRING_ARCH_strsep], 1, [Prevent clang array-bounds warning by not using strsep from bits/string2.h])
+			AC_MSG_RESULT([prevent use of __string2_1bptr_p / strsep / strcmp from bits/string2.h])
+		]
+	)
+	CFLAGS="$save_CFLAGS"
+])
diff --git a/autoconf/ast_gcc_attribute.m4 b/autoconf/ast_gcc_attribute.m4
index bceaa28..4ade814 100644
--- a/autoconf/ast_gcc_attribute.m4
+++ b/autoconf/ast_gcc_attribute.m4
@@ -20,7 +20,7 @@ AC_COMPILE_IFELSE(
 )
 else
 AC_COMPILE_IFELSE(
-	[AC_LANG_PROGRAM([$3 void __attribute__(($2)) *test(void *muffin, ...) {return (void *) 0;}],
+	[AC_LANG_PROGRAM([$3 void __attribute__(($2)) *test(void *muffin, ...) ;],
 			[])],
 	AC_MSG_RESULT(yes)
 	m4_ifval([$4],$4=1)
diff --git a/bridges/bridge_builtin_features.c b/bridges/bridge_builtin_features.c
index b5a6316..d09a7a2 100644
--- a/bridges/bridge_builtin_features.c
+++ b/bridges/bridge_builtin_features.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 426552 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -499,6 +499,10 @@ static int feature_hangup(struct ast_bridge_channel *bridge_channel, void *hook_
 
 static int unload_module(void)
 {
+	ast_bridge_features_unregister(AST_BRIDGE_BUILTIN_HANGUP);
+	ast_bridge_features_unregister(AST_BRIDGE_BUILTIN_AUTOMON);
+	ast_bridge_features_unregister(AST_BRIDGE_BUILTIN_AUTOMIXMON);
+
 	return 0;
 }
 
@@ -508,8 +512,8 @@ static int load_module(void)
 	ast_bridge_features_register(AST_BRIDGE_BUILTIN_AUTOMON, feature_automonitor, NULL);
 	ast_bridge_features_register(AST_BRIDGE_BUILTIN_AUTOMIXMON, feature_automixmonitor, NULL);
 
-	/* Bump up our reference count so we can't be unloaded */
-	ast_module_ref(ast_module_info->self);
+	/* This module cannot be unloaded until shutdown */
+	ast_module_shutdown_ref(ast_module_info->self);
 
 	return AST_MODULE_LOAD_SUCCESS;
 }
diff --git a/bridges/bridge_builtin_interval_features.c b/bridges/bridge_builtin_interval_features.c
index 39a12de..bb07e50 100644
--- a/bridges/bridge_builtin_interval_features.c
+++ b/bridges/bridge_builtin_interval_features.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 397577 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <stdio.h>
 #include <stdlib.h>
diff --git a/bridges/bridge_holding.c b/bridges/bridge_holding.c
index 2253a95..0ef5601 100644
--- a/bridges/bridge_holding.c
+++ b/bridges/bridge_holding.c
@@ -34,7 +34,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419044 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -205,7 +205,11 @@ static void participant_entertainment_start(struct ast_bridge_channel *bridge_ch
 	switch(hc->idle_mode) {
 	case IDLE_MODE_MOH:
 		moh_class = ast_bridge_channel_get_role_option(bridge_channel, "holding_participant", "moh_class");
-		ast_moh_start(bridge_channel->chan, moh_class, NULL);
+		if (ast_moh_start(bridge_channel->chan, moh_class, NULL)) {
+			ast_log(LOG_WARNING, "Failed to start moh, starting silence generator instead\n");
+			hc->idle_mode = IDLE_MODE_SILENCE;
+			hc->silence_generator = ast_channel_start_silence_generator(bridge_channel->chan);
+		}
 		break;
 	case IDLE_MODE_RINGING:
 		ast_indicate(bridge_channel->chan, AST_CONTROL_RINGING);
@@ -428,21 +432,17 @@ static void deferred_action(struct ast_bridge_channel *bridge_channel, const voi
 
 static int unload_module(void)
 {
-	ao2_cleanup(holding_bridge.format_capabilities);
-	holding_bridge.format_capabilities = NULL;
-	return ast_bridge_technology_unregister(&holding_bridge);
+	ast_bridge_technology_unregister(&holding_bridge);
+	return 0;
 }
 
 static int load_module(void)
 {
-	if (!(holding_bridge.format_capabilities = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {
+	if (ast_bridge_technology_register(&holding_bridge)) {
+		unload_module();
 		return AST_MODULE_LOAD_DECLINE;
 	}
-	ast_format_cap_append_by_type(holding_bridge.format_capabilities, AST_MEDIA_TYPE_AUDIO);
-	ast_format_cap_append_by_type(holding_bridge.format_capabilities, AST_MEDIA_TYPE_VIDEO);
-	ast_format_cap_append_by_type(holding_bridge.format_capabilities, AST_MEDIA_TYPE_TEXT);
-
-	return ast_bridge_technology_register(&holding_bridge);
+	return AST_MODULE_LOAD_SUCCESS;
 }
 
 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Holding bridge module");
diff --git a/bridges/bridge_native_rtp.c b/bridges/bridge_native_rtp.c
index 6d47985..7eacc9d 100644
--- a/bridges/bridge_native_rtp.c
+++ b/bridges/bridge_native_rtp.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 427583 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -50,6 +50,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 427583 $")
 struct native_rtp_bridge_data {
 	/*! \brief Framehook used to intercept certain control frames */
 	int id;
+	/*! \brief Set when this framehook has been detached */
+	unsigned int detached;
 };
 
 /*! \brief Internal helper function which gets all RTP information (glue and instances) relating to the given channels */
@@ -157,7 +159,7 @@ static void native_rtp_bridge_start(struct ast_bridge *bridge, struct ast_channe
 		}
 		ast_rtp_instance_set_bridged(instance0, instance1);
 		ast_rtp_instance_set_bridged(instance1, instance0);
-		ast_debug(2, "Locally RTP bridged '%s' and '%s' in stack\n",
+		ast_verb(4, "Locally RTP bridged '%s' and '%s' in stack\n",
 			ast_channel_name(bc0->chan), ast_channel_name(bc1->chan));
 		break;
 
@@ -173,7 +175,7 @@ static void native_rtp_bridge_start(struct ast_bridge *bridge, struct ast_channe
 		if (!target) {
 			glue0->update_peer(bc0->chan, instance1, vinstance1, tinstance1, cap1, 0);
 			glue1->update_peer(bc1->chan, instance0, vinstance0, tinstance0, cap0, 0);
-			ast_debug(2, "Remotely bridged '%s' and '%s' - media will flow directly between them\n",
+			ast_verb(4, "Remotely bridged '%s' and '%s' - media will flow directly between them\n",
 				ast_channel_name(bc0->chan), ast_channel_name(bc1->chan));
 		} else {
 			/*
@@ -261,6 +263,7 @@ 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 native_rtp_bridge_data *native_data = data;
 
 	if (!f || (event != AST_FRAMEHOOK_EVENT_WRITE)) {
 		return f;
@@ -272,13 +275,22 @@ static struct ast_frame *native_rtp_framehook(struct ast_channel *chan, struct a
 		/* native_rtp_bridge_start/stop are not being called from bridging
 		   core so we need to lock the bridge prior to calling these functions
 		   Unfortunately that means unlocking the channel, but as it
-		   should not be modified this should be okay...hopefully */
+		   should not be modified this should be okay... hopefully...
+		   unless this channel is being moved around right now and is in
+		   the process of having this framehook removed (which is fine). To
+		   ensure we then don't stop or start when we shouldn't we consult
+		   the data provided. If this framehook has been detached then the
+		   detached variable will be set. This is safe to check as it is only
+		   manipulated with the bridge lock held. */
 		ast_channel_unlock(chan);
 		ast_bridge_lock(bridge);
-		if (f->subclass.integer == 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)) {
-			native_rtp_bridge_start(bridge, chan);
+		if (!native_data->detached) {
+			if (f->subclass.integer == 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)) {
+				native_rtp_bridge_start(bridge, chan);
+			}
 		}
 		ast_bridge_unlock(bridge);
 		ast_channel_lock(chan);
@@ -310,8 +322,8 @@ static int native_rtp_bridge_compatible(struct ast_bridge *bridge)
 	RAII_VAR(struct ast_rtp_instance *, instance1, NULL, ao2_cleanup);
 	RAII_VAR(struct ast_rtp_instance *, vinstance0, NULL, ao2_cleanup);
 	RAII_VAR(struct ast_rtp_instance *, vinstance1, NULL, ao2_cleanup);
-	RAII_VAR(struct ast_format_cap *, cap0, ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT), ao2_cleanup);
-	RAII_VAR(struct ast_format_cap *, cap1, ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT), ao2_cleanup);
+	RAII_VAR(struct ast_format_cap *, cap0, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_format_cap *, cap1, NULL, ao2_cleanup);
 	int read_ptime0, read_ptime1, write_ptime0, write_ptime1;
 
 	/* We require two channels before even considering native bridging */
@@ -361,6 +373,12 @@ static int native_rtp_bridge_compatible(struct ast_bridge *bridge)
 		return 0;
 	}
 
+	cap0 = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
+	cap1 = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
+	if (!cap0 || !cap1) {
+		return 0;
+	}
+
 	/* Make sure that codecs match */
 	if (glue0->get_codec) {
 		glue0->get_codec(bc0->chan, cap0);
@@ -369,8 +387,8 @@ static int native_rtp_bridge_compatible(struct ast_bridge *bridge)
 		glue1->get_codec(bc1->chan, cap1);
 	}
 	if (ast_format_cap_count(cap0) != 0 && ast_format_cap_count(cap1) != 0 && !ast_format_cap_iscompatible(cap0, cap1)) {
-		struct ast_str *codec_buf0 = ast_str_alloca(64);
-		struct ast_str *codec_buf1 = ast_str_alloca(64);
+		struct ast_str *codec_buf0 = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+		struct ast_str *codec_buf1 = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 		ast_debug(1, "Channel codec0 = %s is not codec1 = %s, cannot native bridge in RTP.\n",
 			ast_format_cap_get_names(cap0, &codec_buf0), ast_format_cap_get_names(cap1, &codec_buf1));
 		return 0;
@@ -397,6 +415,7 @@ static int native_rtp_bridge_framehook_attach(struct ast_bridge_channel *bridge_
 	static struct ast_framehook_interface hook = {
 		.version = AST_FRAMEHOOK_INTERFACE_VERSION,
 		.event_cb = native_rtp_framehook,
+		.destroy_cb = __ao2_cleanup,
 		.consume_cb = native_rtp_framehook_consume,
 		.disable_inheritance = 1,
 	};
@@ -406,10 +425,12 @@ static int native_rtp_bridge_framehook_attach(struct ast_bridge_channel *bridge_
 	}
 
 	ast_channel_lock(bridge_channel->chan);
+	hook.data = ao2_bump(data);
 	data->id = ast_framehook_attach(bridge_channel->chan, &hook);
 	ast_channel_unlock(bridge_channel->chan);
 	if (data->id < 0) {
-		ao2_cleanup(data);
+		/* We need to drop both the reference we hold, and the one the framehook would hold */
+		ao2_ref(data, -2);
 		return -1;
 	}
 
@@ -429,6 +450,7 @@ static void native_rtp_bridge_framehook_detach(struct ast_bridge_channel *bridge
 
 	ast_channel_lock(bridge_channel->chan);
 	ast_framehook_detach(bridge_channel->chan, data->id);
+	data->detached = 1;
 	ast_channel_unlock(bridge_channel->chan);
 	bridge_channel->tech_pvt = NULL;
 }
@@ -474,20 +496,17 @@ static struct ast_bridge_technology native_rtp_bridge = {
 
 static int unload_module(void)
 {
-	ao2_t_ref(native_rtp_bridge.format_capabilities, -1, "Dispose of capabilities in module unload");
-	return ast_bridge_technology_unregister(&native_rtp_bridge);
+	ast_bridge_technology_unregister(&native_rtp_bridge);
+	return 0;
 }
 
 static int load_module(void)
 {
-	if (!(native_rtp_bridge.format_capabilities = ast_format_cap_alloc(0))) {
+	if (ast_bridge_technology_register(&native_rtp_bridge)) {
+		unload_module();
 		return AST_MODULE_LOAD_DECLINE;
 	}
-	ast_format_cap_append_by_type(native_rtp_bridge.format_capabilities, AST_MEDIA_TYPE_AUDIO);
-	ast_format_cap_append_by_type(native_rtp_bridge.format_capabilities, AST_MEDIA_TYPE_VIDEO);
-	ast_format_cap_append_by_type(native_rtp_bridge.format_capabilities, AST_MEDIA_TYPE_TEXT);
-
-	return ast_bridge_technology_register(&native_rtp_bridge);
+	return AST_MODULE_LOAD_SUCCESS;
 }
 
 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Native RTP bridging module");
diff --git a/bridges/bridge_simple.c b/bridges/bridge_simple.c
index 7665547..5704535 100644
--- a/bridges/bridge_simple.c
+++ b/bridges/bridge_simple.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419044 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -76,21 +76,17 @@ static struct ast_bridge_technology simple_bridge = {
 
 static int unload_module(void)
 {
-	ao2_cleanup(simple_bridge.format_capabilities);
-	simple_bridge.format_capabilities = NULL;
-	return ast_bridge_technology_unregister(&simple_bridge);
+	ast_bridge_technology_unregister(&simple_bridge);
+	return 0;
 }
 
 static int load_module(void)
 {
-	if (!(simple_bridge.format_capabilities = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {
+	if (ast_bridge_technology_register(&simple_bridge)) {
+		unload_module();
 		return AST_MODULE_LOAD_DECLINE;
 	}
-	ast_format_cap_append_by_type(simple_bridge.format_capabilities, AST_MEDIA_TYPE_AUDIO);
-	ast_format_cap_append_by_type(simple_bridge.format_capabilities, AST_MEDIA_TYPE_VIDEO);
-	ast_format_cap_append_by_type(simple_bridge.format_capabilities, AST_MEDIA_TYPE_TEXT);
-
-	return ast_bridge_technology_register(&simple_bridge);
+	return AST_MODULE_LOAD_SUCCESS;
 }
 
 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Simple two channel bridging module");
diff --git a/bridges/bridge_softmix.c b/bridges/bridge_softmix.c
index 8fca093..b463f99 100644
--- a/bridges/bridge_softmix.c
+++ b/bridges/bridge_softmix.c
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 423423 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -57,6 +57,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 423423 $")
 
 #define MAX_DATALEN 8096
 
+/*! The minimum sample rate of the bridge. */
+#define SOFTMIX_MIN_SAMPLE_RATE 8000	/* 8 kHz sample rate */
+
 /*! \brief Interval at which mixing will take place. Valid options are 10, 20, and 40. */
 #define DEFAULT_SOFTMIX_INTERVAL 20
 
@@ -96,8 +99,8 @@ struct softmix_channel {
 	struct ast_slinfactory factory;
 	/*! Frame that contains mixed audio to be written out to the channel */
 	struct ast_frame write_frame;
-	/*! Frame that contains mixed audio read from the channel */
-	struct ast_frame read_frame;
+	/*! Current expected read slinear format. */
+	struct ast_format *read_slin_format;
 	/*! DSP for detecting silence */
 	struct ast_dsp *dsp;
 	/*!
@@ -144,11 +147,11 @@ struct softmix_stats {
 	unsigned int sample_rates[16];
 	/*! Each index represents the number of channels using the same index in the sample_rates array.  */
 	unsigned int num_channels[16];
-	/*! the number of channels above the internal sample rate */
+	/*! The number of channels above the internal sample rate */
 	unsigned int num_above_internal_rate;
-	/*! the number of channels at the internal sample rate */
+	/*! The number of channels at the internal sample rate */
 	unsigned int num_at_internal_rate;
-	/*! the absolute highest sample rate supported by any channel in the bridge */
+	/*! The absolute highest sample rate preferred by any channel in the bridge */
 	unsigned int highest_supported_rate;
 	/*! Is the sample rate locked by the bridge, if so what is that rate.*/
 	unsigned int locked_rate;
@@ -181,6 +184,9 @@ static struct softmix_translate_helper_entry *softmix_translate_helper_entry_all
 		return NULL;
 	}
 	entry->dst_format = ao2_bump(dst);
+	/* initialize this to one so that the first time through the cleanup code after
+	   allocation it won't be removed from the entry list */
+	entry->num_times_requested = 1;
 	return entry;
 }
 
@@ -270,11 +276,24 @@ static void softmix_process_write_audio(struct softmix_translate_helper *trans_h
 		for (i = 0; i < sc->write_frame.samples; i++) {
 			ast_slinear_saturated_subtract(&sc->final_buf[i], &sc->our_buf[i]);
 		}
+		/* check to see if any entries exist for the format. if not we'll want
+		   to remove it during cleanup */
+		AST_LIST_TRAVERSE(&trans_helper->entries, entry, entry) {
+			if (ast_format_cmp(entry->dst_format, raw_write_fmt) == AST_FORMAT_CMP_EQUAL) {
+				++entry->num_times_requested;
+				break;
+			}
+		}
 		/* do not do any special write translate optimization if we had to make
 		 * a special mix for them to remove their own audio. */
 		return;
 	}
 
+	/* Attempt to optimize channels using the same translation path/codec. Build a list of entries
+	   of translation paths and track the number of references for each type. Each one of the same
+	   type should be able to use the same out_frame. Since the optimization is only necessary for
+	   multiple channels (>=2) using the same codec make sure resources are allocated only when
+	   needed and released when not (see also softmix_translate_helper_cleanup */
 	AST_LIST_TRAVERSE(&trans_helper->entries, entry, entry) {
 		if (ast_format_cmp(entry->dst_format, raw_write_fmt) == AST_FORMAT_CMP_EQUAL) {
 			entry->num_times_requested++;
@@ -306,19 +325,40 @@ static void softmix_translate_helper_cleanup(struct softmix_translate_helper *tr
 {
 	struct softmix_translate_helper_entry *entry;
 
-	AST_LIST_TRAVERSE(&trans_helper->entries, entry, entry) {
+	AST_LIST_TRAVERSE_SAFE_BEGIN(&trans_helper->entries, entry, entry) {
+		/* if it hasn't been requested then remove it */
+		if (!entry->num_times_requested) {
+			AST_LIST_REMOVE_CURRENT(entry);
+			softmix_translate_helper_free_entry(entry);
+			continue;
+		}
+
 		if (entry->out_frame) {
 			ast_frfree(entry->out_frame);
 			entry->out_frame = NULL;
 		}
+
+		/* nothing is optimized for a single path reference, so there is
+		   no reason to continue to hold onto the codec */
+		if (entry->num_times_requested == 1 && entry->trans_pvt) {
+			ast_translator_free_path(entry->trans_pvt);
+			entry->trans_pvt = NULL;
+		}
+
+		/* for each iteration (a mixing run) in the bridge softmix thread the number
+		   of references to a given entry is recalculated, so reset the number of
+		   times requested */
 		entry->num_times_requested = 0;
 	}
+	AST_LIST_TRAVERSE_SAFE_END;
 }
 
 static void set_softmix_bridge_data(int rate, int interval, struct ast_bridge_channel *bridge_channel, int reset)
 {
 	struct softmix_channel *sc = bridge_channel->tech_pvt;
-	unsigned int channel_read_rate = ast_format_get_sample_rate(ast_channel_rawreadformat(bridge_channel->chan));
+	struct ast_format *slin_format;
+
+	slin_format = ast_format_cache_get_slin_by_rate(rate);
 
 	ast_mutex_lock(&sc->lock);
 	if (reset) {
@@ -334,31 +374,29 @@ static void set_softmix_bridge_data(int rate, int interval, struct ast_bridge_ch
 	 * for the channel.  The translated format may not be a
 	 * static cached format.
 	 */
-	ao2_replace(sc->write_frame.subclass.format, ast_format_cache_get_slin_by_rate(rate));
+	ao2_replace(sc->write_frame.subclass.format, slin_format);
 	sc->write_frame.data.ptr = sc->final_buf;
 	sc->write_frame.datalen = SOFTMIX_DATALEN(rate, interval);
 	sc->write_frame.samples = SOFTMIX_SAMPLES(rate, interval);
 
-	/* Setup read frame parameters */
-	sc->read_frame.frametype = AST_FRAME_VOICE;
 	/*
-	 * NOTE: The read_frame format does not hold a reference because it
+	 * NOTE: The read_slin_format does not hold a reference because it
 	 * will always be a signed linear format.
 	 */
-	sc->read_frame.subclass.format = ast_format_cache_get_slin_by_rate(channel_read_rate);
-	sc->read_frame.data.ptr = sc->our_buf;
-	sc->read_frame.datalen = SOFTMIX_DATALEN(channel_read_rate, interval);
-	sc->read_frame.samples = SOFTMIX_SAMPLES(channel_read_rate, interval);
+	sc->read_slin_format = slin_format;
 
 	/* Setup smoother */
-	ast_slinfactory_init_with_format(&sc->factory, sc->write_frame.subclass.format);
+	ast_slinfactory_init_with_format(&sc->factory, slin_format);
 
 	/* set new read and write formats on channel. */
-	ast_set_read_format(bridge_channel->chan, sc->read_frame.subclass.format);
-	ast_set_write_format(bridge_channel->chan, sc->write_frame.subclass.format);
+	ast_channel_lock(bridge_channel->chan);
+	ast_set_read_format_path(bridge_channel->chan,
+		ast_channel_rawreadformat(bridge_channel->chan), slin_format);
+	ast_channel_unlock(bridge_channel->chan);
+	ast_set_write_format(bridge_channel->chan, slin_format);
 
 	/* set up new DSP.  This is on the read side only right before the read frame enters the smoother.  */
-	sc->dsp = ast_dsp_new_with_rate(channel_read_rate);
+	sc->dsp = ast_dsp_new_with_rate(rate);
 	/* we want to aggressively detect silence to avoid feedback */
 	if (bridge_channel->tech_args.talking_threshold) {
 		ast_dsp_set_threshold(sc->dsp, bridge_channel->tech_args.talking_threshold);
@@ -556,6 +594,28 @@ static void softmix_bridge_write_voice(struct ast_bridge *bridge, struct ast_bri
 
 	/* Write the frame into the conference */
 	ast_mutex_lock(&sc->lock);
+
+	if (ast_format_cmp(frame->subclass.format, sc->read_slin_format) != AST_FORMAT_CMP_EQUAL) {
+		/*
+		 * The incoming frame is not the expected format.  Update
+		 * the channel's translation path to get us slinear from
+		 * the new format for the next frame.
+		 *
+		 * There is the possibility that this frame is an old slinear
+		 * rate frame that was in flight when the softmix bridge
+		 * changed rates.  If so it will self correct on subsequent
+		 * frames.
+		 */
+		ast_channel_lock(bridge_channel->chan);
+		ast_debug(1, "Channel %s wrote unexpected format into bridge.  Got %s, expected %s.\n",
+			ast_channel_name(bridge_channel->chan),
+			ast_format_get_name(frame->subclass.format),
+			ast_format_get_name(sc->read_slin_format));
+		ast_set_read_format_path(bridge_channel->chan, frame->subclass.format,
+			sc->read_slin_format);
+		ast_channel_unlock(bridge_channel->chan);
+	}
+
 	ast_dsp_silence_with_energy(sc->dsp, frame, &totalsilence, &cur_energy);
 
 	if (bridge->softmix.video_mode.mode == AST_BRIDGE_VIDEO_MODE_TALKER_SRC) {
@@ -694,15 +754,19 @@ static void gather_softmix_stats(struct softmix_stats *stats,
 	struct ast_bridge_channel *bridge_channel)
 {
 	int channel_native_rate;
-	int i;
+
 	/* Gather stats about channel sample rates. */
-	channel_native_rate = MAX(ast_format_get_sample_rate(ast_channel_rawwriteformat(bridge_channel->chan)),
+	ast_channel_lock(bridge_channel->chan);
+	channel_native_rate = MAX(SOFTMIX_MIN_SAMPLE_RATE,
 		ast_format_get_sample_rate(ast_channel_rawreadformat(bridge_channel->chan)));
+	ast_channel_unlock(bridge_channel->chan);
 
-	if (channel_native_rate > stats->highest_supported_rate) {
+	if (stats->highest_supported_rate < channel_native_rate) {
 		stats->highest_supported_rate = channel_native_rate;
 	}
-	if (channel_native_rate > softmix_data->internal_rate) {
+	if (softmix_data->internal_rate < channel_native_rate) {
+		int i;
+
 		for (i = 0; i < ARRAY_LEN(stats->sample_rates); i++) {
 			if (stats->sample_rates[i] == channel_native_rate) {
 				stats->num_channels[i]++;
@@ -714,10 +778,11 @@ static void gather_softmix_stats(struct softmix_stats *stats,
 			}
 		}
 		stats->num_above_internal_rate++;
-	} else if (channel_native_rate == softmix_data->internal_rate) {
+	} else if (softmix_data->internal_rate == channel_native_rate) {
 		stats->num_at_internal_rate++;
 	}
 }
+
 /*!
  * \internal
  * \brief Analyse mixing statistics and change bridges internal rate
@@ -729,7 +794,9 @@ static void gather_softmix_stats(struct softmix_stats *stats,
 static unsigned int analyse_softmix_stats(struct softmix_stats *stats, struct softmix_bridge_data *softmix_data)
 {
 	int i;
-	/* Re-adjust the internal bridge sample rate if
+
+	/*
+	 * Re-adjust the internal bridge sample rate if
 	 * 1. The bridge's internal sample rate is locked in at a sample
 	 *    rate other than the current sample rate being used.
 	 * 2. two or more channels support a higher sample rate
@@ -739,9 +806,9 @@ static unsigned int analyse_softmix_stats(struct softmix_stats *stats, struct so
 		/* if the rate is locked by the bridge, only update it if it differs
 		 * from the current rate we are using. */
 		if (softmix_data->internal_rate != stats->locked_rate) {
+			ast_debug(1, "Locking at new rate.  Bridge changed from %u to %u.\n",
+				softmix_data->internal_rate, stats->locked_rate);
 			softmix_data->internal_rate = stats->locked_rate;
-			ast_debug(1, "Bridge is locked in at sample rate %u\n",
-				softmix_data->internal_rate);
 			return 1;
 		}
 	} else if (stats->num_above_internal_rate >= 2) {
@@ -753,42 +820,47 @@ static unsigned int analyse_softmix_stats(struct softmix_stats *stats, struct so
 			if (stats->num_channels[i]) {
 				break;
 			}
-			/* best_rate starts out being the first sample rate
-			 * greater than the internal sample rate that 2 or
-			 * more channels support. */
-			if (stats->num_channels[i] >= 2 && (best_index == -1)) {
-				best_rate = stats->sample_rates[i];
-				best_index = i;
-			/* If it has been detected that multiple rates above
-			 * the internal rate are present, compare those rates
-			 * to each other and pick the highest one two or more
-			 * channels support. */
-			} else if (((best_index != -1) &&
-				(stats->num_channels[i] >= 2) &&
-				(stats->sample_rates[best_index] < stats->sample_rates[i]))) {
-				best_rate = stats->sample_rates[i];
-				best_index = i;
-			/* It is possible that multiple channels exist with native sample
-			 * rates above the internal sample rate, but none of those channels
-			 * have the same rate in common.  In this case, the lowest sample
-			 * rate among those channels is picked. Over time as additional
-			 * statistic runs are made the internal sample rate number will
-			 * adjust to the most optimal sample rate, but it may take multiple
-			 * iterations. */
+			if (2 <= stats->num_channels[i]) {
+				/* Two or more channels support this rate. */
+				if (best_index == -1
+					|| stats->sample_rates[best_index] < stats->sample_rates[i]) {
+					/*
+					 * best_rate starts out being the first sample rate
+					 * greater than the internal sample rate that two or
+					 * more channels support.
+					 *
+					 * or
+					 *
+					 * There are multiple rates above the internal rate
+					 * and this rate is higher than the previous rate two
+					 * or more channels support.
+					 */
+					best_rate = stats->sample_rates[i];
+					best_index = i;
+				}
 			} else if (best_index == -1) {
+				/*
+				 * It is possible that multiple channels exist with native sample
+				 * rates above the internal sample rate, but none of those channels
+				 * have the same rate in common.  In this case, the lowest sample
+				 * rate among those channels is picked. Over time as additional
+				 * statistic runs are made the internal sample rate number will
+				 * adjust to the most optimal sample rate, but it may take multiple
+				 * iterations.
+				 */
 				best_rate = MIN(best_rate, stats->sample_rates[i]);
 			}
 		}
 
-		ast_debug(1, "Bridge changed from %u To %u\n",
+		ast_debug(1, "Multiple above internal rate.  Bridge changed from %u to %u.\n",
 			softmix_data->internal_rate, best_rate);
 		softmix_data->internal_rate = best_rate;
 		return 1;
 	} else if (!stats->num_at_internal_rate && !stats->num_above_internal_rate) {
 		/* In this case, the highest supported rate is actually lower than the internal rate */
-		softmix_data->internal_rate = stats->highest_supported_rate;
-		ast_debug(1, "Bridge changed from %u to %u\n",
+		ast_debug(1, "All below internal rate.  Bridge changed from %u to %u.\n",
 			softmix_data->internal_rate, stats->highest_supported_rate);
+		softmix_data->internal_rate = stats->highest_supported_rate;
 		return 1;
 	}
 	return 0;
@@ -1076,8 +1148,8 @@ static int softmix_bridge_create(struct ast_bridge *bridge)
 		softmix_bridge_data_destroy(softmix_data);
 		return -1;
 	}
-	/* start at 8khz, let it grow from there */
-	softmix_data->internal_rate = 8000;
+	/* start at minimum rate, let it grow from there */
+	softmix_data->internal_rate = SOFTMIX_MIN_SAMPLE_RATE;
 	softmix_data->internal_mixing_interval = DEFAULT_SOFTMIX_INTERVAL;
 
 	bridge->tech_pvt = softmix_data;
@@ -1159,18 +1231,17 @@ static struct ast_bridge_technology softmix_bridge = {
 
 static int unload_module(void)
 {
-	ao2_cleanup(softmix_bridge.format_capabilities);
-	softmix_bridge.format_capabilities = NULL;
-	return ast_bridge_technology_unregister(&softmix_bridge);
+	ast_bridge_technology_unregister(&softmix_bridge);
+	return 0;
 }
 
 static int load_module(void)
 {
-	if (!(softmix_bridge.format_capabilities = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {
+	if (ast_bridge_technology_register(&softmix_bridge)) {
+		unload_module();
 		return AST_MODULE_LOAD_DECLINE;
 	}
-	ast_format_cap_append(softmix_bridge.format_capabilities, ast_format_slin, 0);
-	return ast_bridge_technology_register(&softmix_bridge);
+	return AST_MODULE_LOAD_SUCCESS;
 }
 
 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Multi-party software based channel mixing");
diff --git a/build_tools/cflags.xml b/build_tools/cflags.xml
index 082e8e6..d11fb22 100644
--- a/build_tools/cflags.xml
+++ b/build_tools/cflags.xml
@@ -77,8 +77,41 @@
 			<support_level>extended</support_level>
 		</member>
 		<member name="MALLOC_DEBUG" displayname="Keep Track of Memory Allocations">
+			<conflict>DEBUG_CHAOS</conflict>
 			<support_level>core</support_level>
 		</member>
+		<member name="DEBUG_CHAOS" displayname="Randomly FAIL memory allocations or other operations">
+			<conflict>MALLOC_DEBUG</conflict>
+			<support_level>core</support_level>
+		</member>
+		<member name="ADDRESS_SANITIZER" displayname="Address Sanitizer">
+			<support_level>extended</support_level>
+			<conflict>THREAD_SANITIZER</conflict>
+			<conflict>LEAK_SANITIZER</conflict>
+			<conflict>UNDEFINED_SANITIZER</conflict>
+			<conflict>MALLOC_DEBUG</conflict>
+			<conflict>DEBUG_CHAOS</conflict>
+		</member>
+		<member name="THREAD_SANITIZER" displayname="Thread Sanitizer">
+			<support_level>extended</support_level>
+			<conflict>ADDRESS_SANITIZER</conflict>
+			<conflict>LEAK_SANITIZER</conflict>
+			<conflict>UNDEFINED_SANITIZER</conflict>
+		</member>
+		<member name="LEAK_SANITIZER" displayname="Leak Sanitizer">
+			<support_level>extended</support_level>
+			<conflict>ADDRESS_SANITIZER</conflict>
+			<conflict>THREAD_SANITIZER</conflict>
+			<conflict>UNDEFINED_SANITIZER</conflict>
+			<conflict>MALLOC_DEBUG</conflict>
+			<conflict>DEBUG_CHAOS</conflict>
+		</member>
+		<member name="UNDEFINED_SANITIZER" displayname="Undefined Behavior Sanitizer">
+			<support_level>extended</support_level>
+			<conflict>ADDRESS_SANITIZER</conflict>
+			<conflict>THREAD_SANITIZER</conflict>
+			<conflict>LEAK_SANITIZER</conflict>
+		</member>
 		<member name="BUSYDETECT_TONEONLY" displayname="Enable additional comparision of only the tone duration not the silence part">
 			<conflict>BUSYDETECT_COMPARE_TONE_AND_SILENCE</conflict>
 			<defaultenabled>no</defaultenabled>
diff --git a/build_tools/get_moduleinfo b/build_tools/get_moduleinfo
index 92bc7e9..e4e72bb 100644
--- a/build_tools/get_moduleinfo
+++ b/build_tools/get_moduleinfo
@@ -1,3 +1,4 @@
-/\/\*\*\* MODULEINFO/ {printit=1; next}
-/\*\*\*\// {if (printit) exit}
+/\/\*\*\* +MODULEINFO/ {printit=1; next}
+/<support_level>/ {if (gotsupportlevel) { next }; gotsupportlevel=1}
+/\*\*\*\// {printit=0}
 /.*/ {if (printit) print}
diff --git a/build_tools/make_buildopts_h b/build_tools/make_buildopts_h
index 55d08ba..b9f9af2 100755
--- a/build_tools/make_buildopts_h
+++ b/build_tools/make_buildopts_h
@@ -10,27 +10,37 @@ cat << END
  */
 
 END
+
+if ${GREP} "AST_DEVMODE" makeopts | ${GREP} -q "yes"
+then
+	echo "#define AST_DEVMODE 1"
+	BUILDOPTS="AST_DEVMODE"
+fi
+
 TMP=`${GREP} -e "^MENUSELECT_CFLAGS" menuselect.makeopts | sed 's/MENUSELECT_CFLAGS\=//g' | sed 's/-D//g'`
 for x in ${TMP}; do
 	echo "#define ${x} 1"
+	if test "${x}" = "DONT_OPTIMIZE" \
+			-o "${x}" = "BETTER_BACKTRACES" \
+			-o "${x}" = "LOTS_OF_SPANS" \
+			-o "${x}" = "BUILD_NATIVE" \
+			-o "${x}" = "AO2_DEBUG" \
+			-o "${x}" = "REBUILD_PARSERS" \
+			-o "${x}" = "RADIO_RELAX" \
+			-o "${x}" = "DEBUG_SCHEDULER" \
+			-o "${x}" = "DETECT_DEADLOCKS" \
+			-o "${x}" = "DUMP_SCHEDULER" ; then
+		# These aren't ABI affecting options, keep them out of AST_BUILDOPTS
+		continue
+	fi
 	if test "x${BUILDOPTS}" != "x" ; then
 		BUILDOPTS="${BUILDOPTS}, ${x}"
 	else
 		BUILDOPTS="${x}"
 	fi
 done
-TMP=`${GREP} -e "^MENUSELECT_BUILD_DEPS" menuselect.makeopts | sed 's/MENUSELECT_BUILD_DEPS\=//g'`
-for x in ${TMP}; do
-	x2=`echo ${x} | tr a-z A-Z`
-	echo "#define AST_MODULE_${x2} 1"
-done
-if ${GREP} "AST_DEVMODE" makeopts | ${GREP} -q "yes"
-then
-	echo "#define AST_DEVMODE 1"
-	TMP="${TMP} AST_DEVMODE"
-fi
 
-BUILDSUM=`echo ${TMP} | ${MD5} | cut -c1-32`
+BUILDSUM=`echo ${BUILDOPTS} | ${MD5} | cut -c1-32`
 
 echo "#define AST_BUILDOPT_SUM \"${BUILDSUM}\""
 echo "#define AST_BUILDOPTS \"${BUILDOPTS}\""
diff --git a/build_tools/make_version b/build_tools/make_version
index d11c096..99d5dee 100755
--- a/build_tools/make_version
+++ b/build_tools/make_version
@@ -3,6 +3,7 @@
 AWK=${AWK:-awk}
 GIT=${GIT:-git}
 GREP=${GREP:-grep}
+MAINLINE_BRANCH=13
 
 if [ -f ${1}/.version ]; then
     cat ${1}/.version
@@ -98,16 +99,16 @@ elif [ -d ${1}/.git ]; then
     MODIFIED=""
     SVN_REV=`${GIT} log --pretty=full -1 | ${GREP} -F "git-svn-id:" | sed -e "s/.*\@\([^\s]*\)\s.*/\1/g"`
     if [ -z "$SVN_REV" ]; then
-        VERSION=GIT-`${GIT} describe --long --always --tags --dirty=M 2> /dev/null`
+        VERSION=`${GIT} describe --long --always --tags --dirty=M 2> /dev/null`
         if [ $? -ne 0 ]; then
             if [ "`${GIT} ls-files -m | wc -l`" != "0" ]; then
                 MODIFIED="M"
             fi
             # Some older versions of git do not support all the above
             # options.
-            VERSION=GIT-`${GIT} rev-parse --short --verify HEAD`${MODIFIED}
+            VERSION=`${GIT} rev-parse --short --verify HEAD`${MODIFIED}
         fi
-        echo ${VERSION}
+        echo GIT-${MAINLINE_BRANCH}-${VERSION}
     else
         PARTS=`LANG=C ${GIT} log --pretty=full | ${GREP} -F "git-svn-id:" | head -1 | ${AWK} '{print $2;}' | sed -e s:^.*/svn/$2/:: | sed -e 's:/: :g' | sed -e 's/@.*$//g'`
         BRANCH=0
diff --git a/build_tools/make_version_c b/build_tools/make_version_c
index 3fea6ce..fcbd94e 100755
--- a/build_tools/make_version_c
+++ b/build_tools/make_version_c
@@ -1,4 +1,7 @@
 #!/bin/sh
+
+GREP=${GREP:-grep}
+
 if test ! -f .flavor ; then
     EXTRA=""
 elif test ! -f .version ; then
@@ -9,6 +12,21 @@ else
     aadkflavor=`cat .flavor`
     EXTRA=" (${aadkflavor} ${aadkver})"
 fi
+
+if ${GREP} "AST_DEVMODE" makeopts | ${GREP} -q "yes"
+then
+	BUILDOPTS="AST_DEVMODE"
+fi
+
+TMP=`${GREP} -e "^MENUSELECT_CFLAGS" menuselect.makeopts | sed 's/MENUSELECT_CFLAGS\=//g' | sed 's/-D//g'`
+for x in ${TMP}; do
+	if test "x${BUILDOPTS}" != "x" ; then
+		BUILDOPTS="${BUILDOPTS}, ${x}"
+	else
+		BUILDOPTS="${x}"
+	fi
+done
+
 cat << END
 /*
  * version.c
@@ -23,6 +41,8 @@ static const char asterisk_version[] = "${ASTERISKVERSION}${EXTRA}";
 
 static const char asterisk_version_num[] = "${ASTERISKVERSIONNUM}";
 
+static const char asterisk_build_opts[] = "${BUILDOPTS}";
+
 const char *ast_get_version(void)
 {
 	return asterisk_version;
@@ -33,4 +53,9 @@ const char *ast_get_version_num(void)
 	return asterisk_version_num;
 }
 
+const char *ast_get_build_opts(void)
+{
+	return asterisk_build_opts;
+}
+
 END
diff --git a/build_tools/mkpkgconfig b/build_tools/mkpkgconfig
index fd7d906..9d29b9e 100755
--- a/build_tools/mkpkgconfig
+++ b/build_tools/mkpkgconfig
@@ -22,13 +22,12 @@ else
 fi
 
 ## Clean out CFLAGS for the spec file.
-
-LOCAL_CFLAGS=`echo $CFLAGS | ${EXTREGEX} 's/\s*-pipe\s*//g' | ${EXTREGEX} 's/-[Wmp]\S*\s*//g' | \
-  ${EXTREGEX} 's/-I(include|\.\.\/include) //g' | \
+LOCAL_CFLAGS=`echo $CFLAGS | ${EXTREGEX} 's/-pipe\s*//g' | ${EXTREGEX} 's/-[Wmp]\S*\s*//g' | \
+  ${EXTREGEX} 's/\s+-I(include|\.\.\/include)\s+/ /g' | \
   ${EXTREGEX} 's/-DINSTALL_PREFIX=\S* //g' | \
   ${EXTREGEX} 's/-DASTERISK_VERSION=\S* //g' | \
-  ${EXTREGEX} 's/-DAST(ETCDIR|LIBDIR|VARLIBDIR|VARRUNDIR|SPOOLDIR|LOGDIR|CONFPATH|MODDIR|AGIDIR)=\S* //g'`
-
+  ${EXTREGEX} 's/-DAST(ETCDIR|LIBDIR|VARLIBDIR|VARRUNDIR|SPOOLDIR|LOGDIR|CONFPATH|MODDIR|AGIDIR)=\S* //g' | \
+  ${EXTREGEX} 's/^\s|\s$//g'`
 
 cat <<EOF > "$PPATH/asterisk.pc"
 install_prefix=$INSTALL_PREFIX
diff --git a/cdr/cdr_adaptive_odbc.c b/cdr/cdr_adaptive_odbc.c
index f159b90..e53240a 100644
--- a/cdr/cdr_adaptive_odbc.c
+++ b/cdr/cdr_adaptive_odbc.c
@@ -40,7 +40,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/types.h>
 #include <time.h>
diff --git a/cdr/cdr_csv.c b/cdr/cdr_csv.c
index 7aa6aa7..4ef3ce1 100644
--- a/cdr/cdr_csv.c
+++ b/cdr/cdr_csv.c
@@ -38,7 +38,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/paths.h"	/* use ast_config_AST_LOG_DIR */
 #include "asterisk/config.h"
@@ -58,6 +58,7 @@ static int accountlogs = 1;
 static int loguniqueid = 0;
 static int loguserfield = 0;
 static int loaded = 0;
+static int newcdrcolumns = 0;
 static const char config[] = "cdr.conf";
 
 /* #define CSV_LOGUNIQUEID 1 */
@@ -113,6 +114,7 @@ static int load_config(int reload)
 	usegmtime = 0;
 	loguniqueid = 0;
 	loguserfield = 0;
+	newcdrcolumns = 0;
 
 	if (!(v = ast_variable_browse(cfg, "csv"))) {
 		ast_config_destroy(cfg);
@@ -129,7 +131,10 @@ static int load_config(int reload)
 			loguniqueid = ast_true(v->value);
 		} else if (!strcasecmp(v->name, "loguserfield")) {
 			loguserfield = ast_true(v->value);
+		} else if (!strcasecmp(v->name, "newcdrcolumns")) {
+			newcdrcolumns = ast_true(v->value);
 		}
+
 	}
 	ast_config_destroy(cfg);
 	return 1;
@@ -241,6 +246,11 @@ static int build_csv_record(char *buf, size_t bufsize, struct ast_cdr *cdr)
 	/* append the user field */
 	if(loguserfield)
 		append_string(buf, cdr->userfield,bufsize);
+	if (newcdrcolumns) {
+		append_string(buf, cdr->peeraccount, bufsize);
+		append_string(buf, cdr->linkedid, bufsize);
+		append_int(buf, cdr->sequence, bufsize);
+	}
 	/* If we hit the end of our buffer, log an error */
 	if (strlen(buf) < bufsize - 5) {
 		/* Trim off trailing comma */
diff --git a/cdr/cdr_custom.c b/cdr/cdr_custom.c
index 7a35a00..df76363 100644
--- a/cdr/cdr_custom.c
+++ b/cdr/cdr_custom.c
@@ -45,7 +45,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <time.h>
 
diff --git a/cdr/cdr_manager.c b/cdr/cdr_manager.c
index 2e30b2e..4fd5fdf 100644
--- a/cdr/cdr_manager.c
+++ b/cdr/cdr_manager.c
@@ -38,9 +38,135 @@
 	<support_level>core</support_level>
  ***/
 
+/*** DOCUMENTATION
+	<managerEvent language="en_US" name="Cdr">
+		<managerEventInstance class="EVENT_FLAG_CDR">
+			<synopsis>Raised when a CDR is generated.</synopsis>
+			<syntax>
+				<parameter name="AccountCode">
+					<para>The account code of the Party A channel.</para>
+				</parameter>
+				<parameter name="Source">
+					<para>The Caller ID number associated with the Party A in the CDR.</para>
+				</parameter>
+				<parameter name="Destination">
+					<para>The dialplan extension the Party A was executing.</para>
+				</parameter>
+				<parameter name="DestinationContext">
+					<para>The dialplan context the Party A was executing.</para>
+				</parameter>
+				<parameter name="CallerID">
+					<para>The Caller ID name associated with the Party A in the CDR.</para>
+				</parameter>
+				<parameter name="Channel">
+					<para>The channel name of the Party A.</para>
+				</parameter>
+				<parameter name="DestinationChannel">
+					<para>The channel name of the Party B.</para>
+				</parameter>
+				<parameter name="LastApplication">
+					<para>The last dialplan application the Party A executed.</para>
+				</parameter>
+				<parameter name="LastData">
+					<para>
+						The parameters passed to the last dialplan application the
+						Party A executed.
+					</para>
+				</parameter>
+				<parameter name="StartTime">
+					<para>The time the CDR was created.</para>
+				</parameter>
+				<parameter name="AnswerTime">
+					<para>
+						The earliest of either the time when Party A answered, or
+						the start time of this CDR.
+					</para>
+				</parameter>
+				<parameter name="EndTime">
+					<para>
+						The time when the CDR was finished. This occurs when the
+						Party A hangs up or when the bridge between Party A and
+						Party B is broken.
+					</para>
+				</parameter>
+				<parameter name="Duration">
+					<para>The time, in seconds, of <replaceable>EndTime</replaceable> - <replaceable>StartTime</replaceable>.</para>
+				</parameter>
+				<parameter name="BillableSeconds">
+					<para>The time, in seconds, of <replaceable>AnswerTime</replaceable> - <replaceable>StartTime</replaceable>.</para>
+				</parameter>
+				<parameter name="Disposition">
+					<para>The final known disposition of the CDR.</para>
+					<enumlist>
+						<enum name="NO ANSWER">
+							<para>The channel was not answered. This is the default disposition.</para>
+						</enum>
+						<enum name="FAILED">
+							<para>The channel attempted to dial but the call failed.</para>
+							<note>
+								<para>The congestion setting in <filename>cdr.conf</filename> can result
+								in the <literal>AST_CAUSE_CONGESTION</literal> hang up cause or the
+								<literal>CONGESTION</literal> dial status to map to this disposition.
+								</para>
+							</note>
+						</enum>
+						<enum name="BUSY">
+							<para>The channel attempted to dial but the remote party was busy.</para>
+						</enum>
+						<enum name="ANSWERED">
+							<para>The channel was answered. The hang up cause will no longer
+							impact the disposition of the CDR.</para>
+						</enum>
+						<enum name="CONGESTION">
+							<para>The channel attempted to dial but the remote party was congested.</para>
+						</enum>
+					</enumlist>
+				</parameter>
+				<parameter name="AMAFlags">
+					<para>A flag that informs a billing system how to treat the CDR.</para>
+					<enumlist>
+						<enum name="OMIT">
+							<para>This CDR should be ignored.</para>
+						</enum>
+						<enum name="BILLING">
+							<para>This CDR contains valid billing data.</para>
+						</enum>
+						<enum name="DOCUMENTATION">
+							<para>This CDR is for documentation purposes.</para>
+						</enum>
+					</enumlist>
+				</parameter>
+				<parameter name="UniqueID">
+					<para>A unique identifier for the Party A channel.</para>
+				</parameter>
+				<parameter name="UserField">
+					<para>
+						A user defined field set on the channels. If set on both the Party A
+						and Party B channel, the userfields of both are concatenated and
+						separated by a <literal>;</literal>.
+					</para>
+				</parameter>
+			</syntax>
+			<description>
+				<para>
+					The <replaceable>Cdr</replaceable> event is only raised when the
+					<filename>cdr_manager</filename> backend is loaded and registered with
+					the CDR engine.
+				</para>
+				<note>
+					<para>
+						This event can contain additional fields depending on the configuration
+						provided by <filename>cdr_manager.conf</filename>.
+					</para>
+				</note>
+			</description>
+		</managerEventInstance>
+	</managerEvent>
+ ***/
+
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <time.h>
 
diff --git a/cdr/cdr_odbc.c b/cdr/cdr_odbc.c
index dbd53a2..1524efd 100644
--- a/cdr/cdr_odbc.c
+++ b/cdr/cdr_odbc.c
@@ -44,7 +44,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/config.h"
 #include "asterisk/channel.h"
@@ -64,6 +64,7 @@ enum {
 	CONFIG_DISPOSITIONSTRING = 1 << 2,
 	CONFIG_HRTIME =            1 << 3,
 	CONFIG_REGISTERED =        1 << 4,
+	CONFIG_NEWCDRCOLUMNS =     1 << 5,
 };
 
 static struct ast_flags config = { 0 };
@@ -72,23 +73,29 @@ static SQLHSTMT execute_cb(struct odbc_obj *obj, void *data)
 {
 	struct ast_cdr *cdr = data;
 	SQLRETURN ODBC_res;
-	char sqlcmd[2048] = "", timestr[128];
+	char sqlcmd[2048] = "", timestr[128], new_columns[120] = "", new_values[7] = "";
 	struct ast_tm tm;
 	SQLHSTMT stmt;
+	int i = 0;
 
 	ast_localtime(&cdr->start, &tm, ast_test_flag(&config, CONFIG_USEGMTIME) ? "GMT" : NULL);
 	ast_strftime(timestr, sizeof(timestr), DATE_FORMAT, &tm);
 
+	if (ast_test_flag(&config, CONFIG_NEWCDRCOLUMNS)) {
+		snprintf(new_columns, sizeof(new_columns), "%s", ",peeraccount,linkedid,sequence");
+		snprintf(new_values, sizeof(new_values), "%s", ",?,?,?");
+	}
+
 	if (ast_test_flag(&config, CONFIG_LOGUNIQUEID)) {
 		snprintf(sqlcmd,sizeof(sqlcmd),"INSERT INTO %s "
 		"(calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,"
-		"lastdata,duration,billsec,disposition,amaflags,accountcode,uniqueid,userfield) "
-		"VALUES ({ts '%s'},?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", table, timestr);
+		"lastdata,duration,billsec,disposition,amaflags,accountcode,uniqueid,userfield%s) "
+		"VALUES ({ts '%s'},?,?,?,?,?,?,?,?,?,?,?,?,?,?,? %s)", table, new_columns, timestr, new_values);
 	} else {
 		snprintf(sqlcmd,sizeof(sqlcmd),"INSERT INTO %s "
 		"(calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,lastdata,"
-		"duration,billsec,disposition,amaflags,accountcode) "
-		"VALUES ({ts '%s'},?,?,?,?,?,?,?,?,?,?,?,?,?)", table, timestr);
+		"duration,billsec,disposition,amaflags,accountcode%s) "
+		"VALUES ({ts '%s'},?,?,?,?,?,?,?,?,?,?,?,?,?%s)", table, new_columns, timestr, new_values);
 	}
 
 	ODBC_res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
@@ -134,9 +141,17 @@ static SQLHSTMT execute_cb(struct odbc_obj *obj, void *data)
 	SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->amaflags, 0, NULL);
 	SQLBindParameter(stmt, 13, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->accountcode), 0, cdr->accountcode, 0, NULL);
 
+	i = 14;
 	if (ast_test_flag(&config, CONFIG_LOGUNIQUEID)) {
 		SQLBindParameter(stmt, 14, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->uniqueid), 0, cdr->uniqueid, 0, NULL);
 		SQLBindParameter(stmt, 15, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->userfield), 0, cdr->userfield, 0, NULL);
+		i = 16;
+	}
+
+	if (ast_test_flag(&config, CONFIG_NEWCDRCOLUMNS)) {
+		SQLBindParameter(stmt, i, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->peeraccount), 0, cdr->peeraccount, 0, NULL);
+		SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->linkedid), 0, cdr->linkedid, 0, NULL);
+		SQLBindParameter(stmt, i + 2, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->sequence, 0, NULL);
 	}
 
 	ODBC_res = SQLExecDirect(stmt, (unsigned char *)sqlcmd, SQL_NTS);
@@ -260,6 +275,13 @@ static int odbc_load_module(int reload)
 				ast_set_flag(&config, CONFIG_REGISTERED);
 			}
 		}
+		if (((tmp = ast_variable_retrieve(cfg, "global", "newcdrcolumns"))) && ast_true(tmp)) {
+			ast_set_flag(&config, CONFIG_NEWCDRCOLUMNS);
+			ast_debug(1, "cdr_odbc: Add new cdr fields\n");
+		} else {
+			ast_clear_flag(&config, CONFIG_NEWCDRCOLUMNS);
+			ast_debug(1, "cdr_odbc: Not add new cdr fields\n");
+		}
 	} while (0);
 
 	if (ast_test_flag(&config, CONFIG_REGISTERED) && (!cfg || dsn == NULL || table == NULL)) {
diff --git a/cdr/cdr_pgsql.c b/cdr/cdr_pgsql.c
index c6ffd4d..aea5669 100644
--- a/cdr/cdr_pgsql.c
+++ b/cdr/cdr_pgsql.c
@@ -49,7 +49,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <libpq-fe.h>
 
@@ -143,7 +143,7 @@ static char *handle_cdr_pgsql_status(struct ast_cli_entry *e, int cmd, struct as
 		return NULL;
 	}
 
-	if (a->argc != 3)
+	if (a->argc != e->args)
 		return CLI_SHOWUSAGE;
 
 	if (connected) {
@@ -670,42 +670,20 @@ static int config_module(int reload)
 		version = PQserverVersion(conn);
 
 		if (version >= 70300) {
-			char *schemaname, *tablename;
+			char *schemaname, *tablename, *tmp_schemaname, *tmp_tablename;
 			if (strchr(table, '.')) {
-				schemaname = ast_strdupa(table);
-				tablename = strchr(schemaname, '.');
-				*tablename++ = '\0';
+				tmp_schemaname = ast_strdupa(table);
+				tmp_tablename = strchr(tmp_schemaname, '.');
+				*tmp_tablename++ = '\0';
 			} else {
-				schemaname = "";
-				tablename = table;
+				tmp_schemaname = "";
+				tmp_tablename = table;
 			}
+			tablename = ast_alloca(strlen(tmp_tablename) * 2 + 1);
+			PQescapeStringConn(conn, tablename, tmp_tablename, strlen(tmp_tablename), NULL);
 
-			/* Escape special characters in schemaname */
-			if (strchr(schemaname, '\\') || strchr(schemaname, '\'')) {
-				char *tmp = schemaname, *ptr;
-
-				ptr = schemaname = ast_alloca(strlen(tmp) * 2 + 1);
-				for (; *tmp; tmp++) {
-					if (strchr("\\'", *tmp)) {
-						*ptr++ = *tmp;
-					}
-					*ptr++ = *tmp;
-				}
-				*ptr = '\0';
-			}
-			/* Escape special characters in tablename */
-			if (strchr(tablename, '\\') || strchr(tablename, '\'')) {
-				char *tmp = tablename, *ptr;
-
-				ptr = tablename = ast_alloca(strlen(tmp) * 2 + 1);
-				for (; *tmp; tmp++) {
-					if (strchr("\\'", *tmp)) {
-						*ptr++ = *tmp;
-					}
-					*ptr++ = *tmp;
-				}
-				*ptr = '\0';
-			}
+			schemaname = ast_alloca(strlen(tmp_schemaname) * 2 + 1);
+			PQescapeStringConn(conn, schemaname, tmp_schemaname, strlen(tmp_schemaname), NULL);
 
 			snprintf(sqlcmd, sizeof(sqlcmd), "SELECT a.attname, t.typname, a.attlen, a.attnotnull, d.adsrc, a.atttypmod FROM (((pg_catalog.pg_class c INNER JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace AND c.relname = '%s' AND n.nspname = %s%s%s) INNER JOIN pg_catalog.pg_attribute a ON (NOT a.attisdropped) AND a.attnum > 0 AND a.attrelid = c.oid) INNER JOIN pg_catalog.pg_type t ON t.oid = a.atttypid) LEFT OUTER JOIN pg_attrdef d ON a.atthasdef AND d.adrelid = a.attrelid AND d.adnum = a [...]
 				tablename,
diff --git a/cdr/cdr_radius.c b/cdr/cdr_radius.c
index 1cb5541..f590c76 100644
--- a/cdr/cdr_radius.c
+++ b/cdr/cdr_radius.c
@@ -39,7 +39,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #ifdef FREERADIUS_CLIENT
 #include <freeradius-client.h>
diff --git a/cdr/cdr_sqlite.c b/cdr/cdr_sqlite.c
index 1b28b6c..88557b6 100644
--- a/cdr/cdr_sqlite.c
+++ b/cdr/cdr_sqlite.c
@@ -43,7 +43,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sqlite.h>
 
diff --git a/cdr/cdr_sqlite3_custom.c b/cdr/cdr_sqlite3_custom.c
index 152afc2..b5cb391 100644
--- a/cdr/cdr_sqlite3_custom.c
+++ b/cdr/cdr_sqlite3_custom.c
@@ -39,7 +39,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sqlite3.h>
 
diff --git a/cdr/cdr_syslog.c b/cdr/cdr_syslog.c
index 6997d17..458721a 100644
--- a/cdr/cdr_syslog.c
+++ b/cdr/cdr_syslog.c
@@ -43,7 +43,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/lock.h"
diff --git a/cdr/cdr_tds.c b/cdr/cdr_tds.c
index b71a15e..7e44983 100644
--- a/cdr/cdr_tds.c
+++ b/cdr/cdr_tds.c
@@ -64,7 +64,7 @@ CREATE TABLE [dbo].[cdr] (
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/config.h"
 #include "asterisk/channel.h"
diff --git a/cel/cel_custom.c b/cel/cel_custom.c
index 9fc2e7f..edb0096 100644
--- a/cel/cel_custom.c
+++ b/cel/cel_custom.c
@@ -35,7 +35,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/paths.h"
 #include "asterisk/channel.h"
diff --git a/cel/cel_manager.c b/cel/cel_manager.c
index b3b5f0f..b3ff6a5 100644
--- a/cel/cel_manager.c
+++ b/cel/cel_manager.c
@@ -33,9 +33,169 @@
 	<support_level>core</support_level>
  ***/
 
+/*** DOCUMENTATION
+	<managerEvent language="en_US" name="CEL">
+		<managerEventInstance class="EVENT_FLAG_CEL">
+			<synopsis>Raised when a Channel Event Log is generated for a channel.</synopsis>
+			<syntax>
+				<parameter name="EventName">
+					<para>
+						The name of the CEL event being raised. This can include
+						both the system defined CEL events, as well as user defined
+						events.
+					</para>
+					<note>
+						<para>All events listed here may not be raised, depending
+						on the configuration in <filename>cel.conf</filename>.</para>
+					</note>
+					<enumlist>
+						<enum name="CHAN_START">
+							<para>A channel was created.</para>
+						</enum>
+						<enum name="CHAN_END">
+							<para>A channel was terminated.</para>
+						</enum>
+						<enum name="ANSWER">
+							<para>A channel answered.</para>
+						</enum>
+						<enum name="HANGUP">
+							<para>A channel was hung up.</para>
+						</enum>
+						<enum name="BRIDGE_ENTER">
+							<para>A channel entered a bridge.</para>
+						</enum>
+						<enum name="BRIDGE_EXIT">
+							<para>A channel left a bridge.</para>
+						</enum>
+						<enum name="APP_START">
+							<para>A channel entered into a tracked application.</para>
+						</enum>
+						<enum name="APP_END">
+							<para>A channel left a tracked application.</para>
+						</enum>
+						<enum name="PARK_START">
+							<para>A channel was parked.</para>
+						</enum>
+						<enum name="PARK_END">
+							<para>A channel was unparked.</para>
+						</enum>
+						<enum name="BLINDTRANSFER">
+							<para>A channel initiated a blind transfer.</para>
+						</enum>
+						<enum name="ATTENDEDTRANSFER">
+							<para>A channel initiated an attended transfer.</para>
+						</enum>
+						<enum name="PICKUP">
+							<para>A channel initated a call pickup.</para>
+						</enum>
+						<enum name="FORWARD">
+							<para>A channel is being forwarded to another destination.</para>
+						</enum>
+						<enum name="LINKEDID_END">
+							<para>The linked ID associated with this channel is being retired.</para>
+						</enum>
+						<enum name="LOCAL_OPTIMIZE">
+							<para>A Local channel optimization has occurred.</para>
+						</enum>
+						<enum name="USER_DEFINED">
+							<para>A user defined type.</para>
+							<note>
+								<para>
+									This event is only present if <literal>show_user_defined</literal>
+									in <filename>cel.conf</filename> is <literal>True</literal>. Otherwise,
+									the user defined event will be placed directly in the
+									<replaceable>EventName</replaceable> field.
+								</para>
+							</note>
+						</enum>
+					</enumlist>
+				</parameter>
+				<parameter name="AccountCode">
+					<para>The channel's account code.</para>
+				</parameter>
+				<parameter name="CallerIDnum">
+					<para>The Caller ID number.</para>
+				</parameter>
+				<parameter name="CallerIDname">
+					<para>The Caller ID name.</para>
+				</parameter>
+				<parameter name="CallerIDani">
+					<para>The Caller ID Automatic Number Identification.</para>
+				</parameter>
+				<parameter name="CallerIDrdnis">
+					<para>The Caller ID Redirected Dialed Number Identification Service.</para>
+				</parameter>
+				<parameter name="CallerIDdnid">
+					<para>The Caller ID Dialed Number Identifier.</para>
+				</parameter>
+				<parameter name="Exten">
+					<para>The dialplan extension the channel is currently executing in.</para>
+				</parameter>
+				<parameter name="Context">
+					<para>The dialplan context the channel is currently executing in.</para>
+				</parameter>
+				<parameter name="Application">
+					<para>The dialplan application the channel is currently executing.</para>
+				</parameter>
+				<parameter name="AppData">
+					<para>The arguments passed to the dialplan <replaceable>Application</replaceable>.</para>
+				</parameter>
+				<parameter name="EventTime">
+					<para>The time the CEL event occurred.</para>
+				</parameter>
+				<parameter name="AMAFlags">
+					<para>A flag that informs a billing system how to treat the CEL.</para>
+					<enumlist>
+						<enum name="OMIT">
+							<para>This event should be ignored.</para>
+						</enum>
+						<enum name="BILLING">
+							<para>This event contains valid billing data.</para>
+						</enum>
+						<enum name="DOCUMENTATION">
+							<para>This event is for documentation purposes.</para>
+						</enum>
+					</enumlist>
+				</parameter>
+				<parameter name="UniqueID">
+					<para>The unique ID of the channel.</para>
+				</parameter>
+				<parameter name="LinkedID">
+					<para>The linked ID of the channel, which ties this event to other related channel's events.</para>
+				</parameter>
+				<parameter name="UserField">
+					<para>
+						A user defined field set on a channel, containing arbitrary
+						application specific data.
+					</para>
+				</parameter>
+				<parameter name="Peer">
+					<para>
+						If this channel is in a bridge, the channel that it is in
+						a bridge with.
+					</para>
+				</parameter>
+				<parameter name="PeerAccount">
+					<para>
+						If this channel is in a bridge, the accountcode of the
+						channel it is in a bridge with.
+					</para>
+				</parameter>
+				<parameter name="Extra">
+					<para>
+						Some events will have event specific data that accompanies the CEL record.
+						This extra data is JSON encoded, and is dependent on the event in
+						question.
+					</para>
+				</parameter>
+			</syntax>
+		</managerEventInstance>
+	</managerEvent>
+ ***/
+
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/channel.h"
 #include "asterisk/cel.h"
diff --git a/cel/cel_odbc.c b/cel/cel_odbc.c
index 62e198e..11e3330 100644
--- a/cel/cel_odbc.c
+++ b/cel/cel_odbc.c
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 427954 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/types.h>
 #include <time.h>
diff --git a/cel/cel_pgsql.c b/cel/cel_pgsql.c
index c61024e..61e3c8d 100644
--- a/cel/cel_pgsql.c
+++ b/cel/cel_pgsql.c
@@ -44,7 +44,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <libpq-fe.h>
 
@@ -100,7 +100,7 @@ static AST_RWLIST_HEAD_STATIC(psql_columns, columns);
 		/* Lengthen buffer, if necessary */ \
 		if (ast_str_strlen(sql) + size + 1 > ast_str_size(sql)) { \
 			if (ast_str_make_space(&sql, ((ast_str_size(sql) + size + 3) / 512 + 1) * 512) != 0) { \
-				ast_log(LOG_ERROR, "Unable to allocate sufficient memory.  Insert CDR failed.\n"); \
+				ast_log(LOG_ERROR, "Unable to allocate sufficient memory.  Insert CEL '%s:%s' failed.\n", pghostname, table); \
 				ast_free(sql); \
 				ast_free(sql2); \
 				AST_RWLIST_UNLOCK(&psql_columns); \
@@ -113,7 +113,7 @@ static AST_RWLIST_HEAD_STATIC(psql_columns, columns);
 	do { \
 		if (ast_str_strlen(sql2) + size + 1 > ast_str_size(sql2)) { \
 			if (ast_str_make_space(&sql2, ((ast_str_size(sql2) + size + 3) / 512 + 1) * 512) != 0) { \
-				ast_log(LOG_ERROR, "Unable to allocate sufficient memory.  Insert CDR failed.\n"); \
+				ast_log(LOG_ERROR, "Unable to allocate sufficient memory.  Insert CEL '%s:%s' failed.\n", pghostname, table); \
 				ast_free(sql); \
 				ast_free(sql2); \
 				AST_RWLIST_UNLOCK(&psql_columns); \
diff --git a/cel/cel_radius.c b/cel/cel_radius.c
index b1dc167..79caf22 100644
--- a/cel/cel_radius.c
+++ b/cel/cel_radius.c
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Rev: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Rev$")
 
 #ifdef FREERADIUS_CLIENT
 #include <freeradius-client.h>
diff --git a/cel/cel_sqlite3_custom.c b/cel/cel_sqlite3_custom.c
index b27a94f..afa99f7 100644
--- a/cel/cel_sqlite3_custom.c
+++ b/cel/cel_sqlite3_custom.c
@@ -41,7 +41,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sqlite3.h>
 
@@ -63,7 +63,6 @@ AST_MUTEX_DEFINE_STATIC(lock);
 
 static const char config_file[] = "cel_sqlite3_custom.conf";
 
-static const char name[] = "cel_sqlite3_custom";
 static sqlite3 *db = NULL;
 
 static char table[80];
diff --git a/cel/cel_tds.c b/cel/cel_tds.c
index 786a85e..fa3eade 100644
--- a/cel/cel_tds.c
+++ b/cel/cel_tds.c
@@ -61,7 +61,7 @@ CREATE TABLE [dbo].[cel] (
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <time.h>
 #include <math.h>
diff --git a/channels/Makefile b/channels/Makefile
index b24478a..d1b895e 100644
--- a/channels/Makefile
+++ b/channels/Makefile
@@ -20,15 +20,15 @@ all: _all
 include $(ASTTOPDIR)/Makefile.moddir_rules
 
 ifneq ($(findstring $(OSARCH), mingw32 cygwin ),)
-  LIBS+= -lres_monitor.so -lres_features.so
+  LIBS+= -lres_monitor.so
 endif
 
 clean::
 	$(MAKE) -C misdn clean
-	rm -f dahdi/*.o dahdi/*.i
-	rm -f sip/*.o sip/*.i
-	rm -f iax2/*.o iax2/*.i
-	rm -f pjsip/*.o pjsip/*.i
+	rm -f dahdi/*.o dahdi/*.i dahdi/*.gcda dahdi/*.gcno
+	rm -f sip/*.o sip/*.i sip/*.gcda sip/*.gcno
+	rm -f iax2/*.o iax2/*.i iax2/*.gcda iax2/*.gcno
+	rm -f pjsip/*.o pjsip/*.i pjsip/*.gcda pjsip/*.gcno
 
 $(if $(filter chan_iax2,$(EMBEDDED_MODS)),modules.link,chan_iax2.so): $(subst .c,.o,$(wildcard iax2/*.c))
 $(subst .c,.o,$(wildcard iax2/*.c)): _ASTCFLAGS+=$(call MOD_ASTCFLAGS,chan_iax2)
diff --git a/channels/chan_alsa.c b/channels/chan_alsa.c
index acb5e19..6508a1e 100644
--- a/channels/chan_alsa.c
+++ b/channels/chan_alsa.c
@@ -39,7 +39,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <fcntl.h>
 #include <sys/ioctl.h>
@@ -620,7 +620,7 @@ static struct ast_channel *alsa_request(const char *type, struct ast_format_cap
 	struct ast_channel *tmp = NULL;
 
 	if (ast_format_cap_iscompatible_format(cap, ast_format_slin) == AST_FORMAT_CMP_NOT_EQUAL) {
-		struct ast_str *codec_buf = ast_str_alloca(64);
+		struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 		ast_log(LOG_NOTICE, "Asked to get a channel of format '%s'\n", ast_format_cap_get_names(cap, &codec_buf));
 		return NULL;
 	}
diff --git a/channels/chan_bridge_media.c b/channels/chan_bridge_media.c
index 13d096e..d7eeebf 100644
--- a/channels/chan_bridge_media.c
+++ b/channels/chan_bridge_media.c
@@ -34,7 +34,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/channel.h"
 #include "asterisk/bridge.h"
diff --git a/channels/chan_console.c b/channels/chan_console.c
index 84c30f9..02d6887 100644
--- a/channels/chan_console.c
+++ b/channels/chan_console.c
@@ -62,7 +62,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 427557 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/signal.h>  /* SIGURG */
 
@@ -479,7 +479,7 @@ static struct ast_channel *console_request(const char *type, struct ast_format_c
 	}
 
 	if (!(ast_format_cap_iscompatible(cap, console_tech.capabilities))) {
-		struct ast_str *cap_buf = ast_str_alloca(64);
+		struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 		ast_log(LOG_NOTICE, "Channel requested with unsupported format(s): '%s'\n",
 			ast_format_cap_get_names(cap, &cap_buf));
 		goto return_unref;
diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c
index 74387a5..ef0ad65 100644
--- a/channels/chan_dahdi.c
+++ b/channels/chan_dahdi.c
@@ -54,7 +54,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428687 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #if defined(__NetBSD__) || defined(__FreeBSD__)
 #include <pthread.h>
@@ -600,14 +600,6 @@ static int restart_monitor(void);
 
 static int dahdi_sendtext(struct ast_channel *c, const char *text);
 
-static void mwi_event_cb(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
-{
-	/* This module does not handle MWI in an event-based manner.  However, it
-	 * subscribes to MWI for each mailbox that is configured so that the core
-	 * knows that we care about it.  Then, chan_dahdi will get the MWI from the
-	 * event cache instead of checking the mailbox directly. */
-}
-
 /*! \brief Avoid the silly dahdi_getevent which ignores a bunch of events */
 static inline int dahdi_get_event(int fd)
 {
@@ -919,6 +911,7 @@ static struct dahdi_chan_conf dahdi_chan_conf_default(void)
 			.privateprefix = "",
 			.unknownprefix = "",
 			.colp_send = SIG_PRI_COLP_UPDATE,
+			.force_restart_unavailable_chans = 1,
 			.resetinterval = -1,
 		},
 #endif
@@ -1301,13 +1294,21 @@ static int my_start_cid_detect(void *pvt, int cid_signalling)
 	return 0;
 }
 
+static int restore_gains(struct dahdi_pvt *p);
+
 static int my_stop_cid_detect(void *pvt)
 {
 	struct dahdi_pvt *p = pvt;
 	int index = SUB_REAL;
-	if (p->cs)
+
+	if (p->cs) {
 		callerid_free(p->cs);
+	}
+
+	/* Restore linear mode after Caller*ID processing */
 	dahdi_setlinear(p->subs[index].dfd, p->subs[index].linear);
+	restore_gains(p);
+
 	return 0;
 }
 
@@ -1383,7 +1384,6 @@ static int my_get_callerid(void *pvt, char *namebuf, char *numbuf, enum analog_e
 }
 
 static const char *event2str(int event);
-static int restore_gains(struct dahdi_pvt *p);
 
 static int my_distinctive_ring(struct ast_channel *chan, void *pvt, int idx, int *ringdata)
 {
@@ -1396,7 +1396,7 @@ static int my_distinctive_ring(struct ast_channel *chan, void *pvt, int idx, int
 	int i;
 	int res;
 	int checkaftercid = 0;
-
+	const char *matched_context;
 	struct dahdi_pvt *p = pvt;
 	struct analog_pvt *analog_p = p->sig_pvt;
 
@@ -1406,46 +1406,44 @@ static int my_distinctive_ring(struct ast_channel *chan, void *pvt, int idx, int
 		checkaftercid = 1;
 	}
 
-	/* We must have a ring by now, so, if configured, lets try to listen for
-	 * distinctive ringing */
+	/* We must have a ring by now so lets try to listen for distinctive ringing */
 	if ((checkaftercid && distinctiveringaftercid) || !checkaftercid) {
 		/* Clear the current ring data array so we don't have old data in it. */
 		for (receivedRingT = 0; receivedRingT < RING_PATTERNS; receivedRingT++)
 			ringdata[receivedRingT] = 0;
 		receivedRingT = 0;
-		if (checkaftercid && distinctiveringaftercid)
+
+		if (checkaftercid && distinctiveringaftercid) {
 			ast_verb(3, "Detecting post-CID distinctive ring\n");
-		/* Check to see if context is what it should be, if not set to be. */
-		else if (strcmp(p->context,p->defcontext) != 0) {
-			ast_copy_string(p->context, p->defcontext, sizeof(p->context));
-			ast_channel_context_set(chan, p->defcontext);
 		}
 
 		for (;;) {
 			i = DAHDI_IOMUX_READ | DAHDI_IOMUX_SIGEVENT;
-			if ((res = ioctl(p->subs[idx].dfd, DAHDI_IOMUX, &i))) {
+			res = ioctl(p->subs[idx].dfd, DAHDI_IOMUX, &i);
+			if (res) {
 				ast_log(LOG_WARNING, "I/O MUX failed: %s\n", strerror(errno));
 				ast_hangup(chan);
 				return 1;
 			}
 			if (i & DAHDI_IOMUX_SIGEVENT) {
 				res = dahdi_get_event(p->subs[idx].dfd);
+				ast_debug(3, "Got event %d (%s)...\n", res, event2str(res));
 				if (res == DAHDI_EVENT_NOALARM) {
 					p->inalarm = 0;
 					analog_p->inalarm = 0;
-				}
-				ast_log(LOG_NOTICE, "Got event %d (%s)...\n", res, event2str(res));
-				res = 0;
-				/* Let us detect distinctive ring */
-
-				ringdata[receivedRingT] = analog_p->ringt;
+				} else if (res == DAHDI_EVENT_RINGOFFHOOK) {
+					/* Let us detect distinctive ring */
+					ringdata[receivedRingT] = analog_p->ringt;
 
-				if (analog_p->ringt < analog_p->ringt_base/2)
-					break;
-				/* Increment the ringT counter so we can match it against
-				   values in chan_dahdi.conf for distinctive ring */
-				if (++receivedRingT == RING_PATTERNS)
-					break;
+					if (analog_p->ringt < analog_p->ringt_base / 2) {
+						break;
+					}
+					/* Increment the ringT counter so we can match it against
+					   values in chan_dahdi.conf for distinctive ring */
+					if (++receivedRingT == RING_PATTERNS) {
+						break;
+					}
+				}
 			} else if (i & DAHDI_IOMUX_READ) {
 				res = read(p->subs[idx].dfd, buf, sizeof(buf));
 				if (res < 0) {
@@ -1458,51 +1456,55 @@ static int my_distinctive_ring(struct ast_channel *chan, void *pvt, int idx, int
 				}
 				if (analog_p->ringt > 0) {
 					if (!(--analog_p->ringt)) {
-						res = -1;
 						break;
 					}
 				}
 			}
 		}
 	}
-	if ((checkaftercid && usedistinctiveringdetection) || !checkaftercid) {
-		/* this only shows up if you have n of the dring patterns filled in */
-		ast_verb(3, "Detected ring pattern: %d,%d,%d\n",ringdata[0],ringdata[1],ringdata[2]);
-		for (counter = 0; counter < 3; counter++) {
-		/* Check to see if the rings we received match any of the ones in chan_dahdi.conf for this channel */
-			distMatches = 0;
-			/* this only shows up if you have n of the dring patterns filled in */
-			ast_verb(3, "Checking %d,%d,%d\n",
-					p->drings.ringnum[counter].ring[0],
-					p->drings.ringnum[counter].ring[1],
-					p->drings.ringnum[counter].ring[2]);
-			for (counter1 = 0; counter1 < 3; counter1++) {
-				ast_verb(3, "Ring pattern check range: %d\n", p->drings.ringnum[counter].range);
-				if (p->drings.ringnum[counter].ring[counter1] == -1) {
-					ast_verb(3, "Pattern ignore (-1) detected, so matching pattern %d regardless.\n",
-					ringdata[counter1]);
-					distMatches++;
-				} else if (ringdata[counter1] <= (p->drings.ringnum[counter].ring[counter1] + p->drings.ringnum[counter].range) &&
-										ringdata[counter1] >= (p->drings.ringnum[counter].ring[counter1] - p->drings.ringnum[counter].range)) {
-					ast_verb(3, "Ring pattern matched in range: %d to %d\n",
-					(p->drings.ringnum[counter].ring[counter1] - p->drings.ringnum[counter].range),
-					(p->drings.ringnum[counter].ring[counter1] + p->drings.ringnum[counter].range));
-					distMatches++;
-				}
-			}
 
-			if (distMatches == 3) {
-				/* The ring matches, set the context to whatever is for distinctive ring.. */
-				ast_copy_string(p->context, S_OR(p->drings.ringContext[counter].contextData, p->defcontext), sizeof(p->context));
-				ast_channel_context_set(chan, S_OR(p->drings.ringContext[counter].contextData, p->defcontext));
-				ast_verb(3, "Distinctive Ring matched context %s\n",p->context);
+	/* Check to see if the rings we received match any of the ones in chan_dahdi.conf for this channel */
+	ast_verb(3, "Detected ring pattern: %d,%d,%d\n", ringdata[0], ringdata[1], ringdata[2]);
+	matched_context = p->defcontext;
+	for (counter = 0; counter < 3; counter++) {
+		int range = p->drings.ringnum[counter].range;
+
+		distMatches = 0;
+		ast_verb(3, "Checking %d,%d,%d with +/- %d range\n",
+			p->drings.ringnum[counter].ring[0],
+			p->drings.ringnum[counter].ring[1],
+			p->drings.ringnum[counter].ring[2],
+			range);
+		for (counter1 = 0; counter1 < 3; counter1++) {
+			int ring = p->drings.ringnum[counter].ring[counter1];
+
+			if (ring == -1) {
+				ast_verb(3, "Pattern ignore (-1) detected, so matching pattern %d regardless.\n",
+					ringdata[counter1]);
+				distMatches++;
+			} else if (ring - range <= ringdata[counter1] && ringdata[counter1] <= ring + range) {
+				ast_verb(3, "Ring pattern %d is in range: %d to %d\n",
+					ringdata[counter1], ring - range, ring + range);
+				distMatches++;
+			} else {
+				/* The current dring pattern cannot match. */
 				break;
 			}
 		}
+
+		if (distMatches == 3) {
+			/* The ring matches, set the context to whatever is for distinctive ring.. */
+			matched_context = S_OR(p->drings.ringContext[counter].contextData, p->defcontext);
+			ast_verb(3, "Matched Distinctive Ring context %s\n", matched_context);
+			break;
+		}
+	}
+
+	/* Set selected distinctive ring context if not already set. */
+	if (strcmp(p->context, matched_context) != 0) {
+		ast_copy_string(p->context, matched_context, sizeof(p->context));
+		ast_channel_context_set(chan, matched_context);
 	}
-	/* Restore linear mode (if appropriate) for Caller*ID processing */
-	dahdi_setlinear(p->subs[idx].dfd, p->subs[idx].linear);
-	restore_gains(p);
 
 	return 0;
 }
@@ -4197,7 +4199,7 @@ static int dahdi_digit_begin(struct ast_channel *chan, char digit)
 {
 	struct dahdi_pvt *pvt;
 	int idx;
-	int dtmf = -1;
+	int dtmf;
 	int res;
 
 	pvt = ast_channel_tech_pvt(chan);
@@ -4220,8 +4222,11 @@ static int dahdi_digit_begin(struct ast_channel *chan, char digit)
 		break;
 	}
 #endif
-	if ((dtmf = digit_to_dtmfindex(digit)) == -1)
+	dtmf = digit_to_dtmfindex(digit);
+	if (dtmf == -1) {
+		/* Not a valid DTMF digit */
 		goto out;
+	}
 
 	if (pvt->pulse || ioctl(pvt->subs[SUB_REAL].dfd, DAHDI_SENDTONE, &dtmf)) {
 		char dial_str[] = { 'T', digit, '\0' };
@@ -4231,10 +4236,19 @@ static int dahdi_digit_begin(struct ast_channel *chan, char digit)
 			pvt->dialing = 1;
 		}
 	} else {
-		ast_debug(1, "Channel %s started VLDTMF digit '%c'\n",
-			ast_channel_name(chan), digit);
 		pvt->dialing = 1;
 		pvt->begindigit = digit;
+
+		/* Flush the write buffer in DAHDI to start sending the digit immediately. */
+		dtmf = DAHDI_FLUSH_WRITE;
+		res = ioctl(pvt->subs[SUB_REAL].dfd, DAHDI_FLUSH, &dtmf);
+		if (res) {
+			ast_log(LOG_WARNING, "Unable to flush the DAHDI write buffer to send DTMF on channel %d: %s\n",
+				pvt->channel, strerror(errno));
+		}
+
+		ast_debug(1, "Channel %s started VLDTMF digit '%c'\n",
+			ast_channel_name(chan), digit);
 	}
 
 out:
@@ -8771,37 +8785,52 @@ static int my_dahdi_write(struct dahdi_pvt *p, unsigned char *buf, int len, int
 
 static int dahdi_write(struct ast_channel *ast, struct ast_frame *frame)
 {
-	struct dahdi_pvt *p = ast_channel_tech_pvt(ast);
+	struct dahdi_pvt *p;
 	int res;
 	int idx;
+
+	/* Write a frame of (presumably voice) data */
+	if (frame->frametype != AST_FRAME_VOICE) {
+		if (frame->frametype != AST_FRAME_IMAGE) {
+			ast_log(LOG_WARNING, "Don't know what to do with frame type '%u'\n",
+				frame->frametype);
+		}
+		return 0;
+	}
+
+	/* Return if it's not valid data */
+	if (!frame->data.ptr || !frame->datalen) {
+		return 0;
+	}
+
+	p = ast_channel_tech_pvt(ast);
+	ast_mutex_lock(&p->lock);
+
 	idx = dahdi_get_index(ast, p, 0);
 	if (idx < 0) {
+		ast_mutex_unlock(&p->lock);
 		ast_log(LOG_WARNING, "%s doesn't really exist?\n", ast_channel_name(ast));
 		return -1;
 	}
 
-	/* Write a frame of (presumably voice) data */
-	if (frame->frametype != AST_FRAME_VOICE) {
-		if (frame->frametype != AST_FRAME_IMAGE)
-			ast_log(LOG_WARNING, "Don't know what to do with frame type '%u'\n", frame->frametype);
-		return 0;
-	}
 	if (p->dialing) {
-		ast_debug(5, "Dropping frame since I'm still dialing on %s...\n",ast_channel_name(ast));
+		ast_mutex_unlock(&p->lock);
+		ast_debug(5, "Dropping frame since I'm still dialing on %s...\n",
+			ast_channel_name(ast));
 		return 0;
 	}
 	if (!p->owner) {
-		ast_debug(5, "Dropping frame since there is no active owner on %s...\n",ast_channel_name(ast));
+		ast_mutex_unlock(&p->lock);
+		ast_debug(5, "Dropping frame since there is no active owner on %s...\n",
+			ast_channel_name(ast));
 		return 0;
 	}
 	if (p->cidspill) {
+		ast_mutex_unlock(&p->lock);
 		ast_debug(5, "Dropping frame since I've still got a callerid spill on %s...\n",
 			ast_channel_name(ast));
 		return 0;
 	}
-	/* Return if it's not valid data */
-	if (!frame->data.ptr || !frame->datalen)
-		return 0;
 
 	if (ast_format_cmp(frame->subclass.format, ast_format_slin) == AST_FORMAT_CMP_EQUAL) {
 		if (!p->subs[idx].linear) {
@@ -8822,10 +8851,12 @@ static int dahdi_write(struct ast_channel *ast, struct ast_frame *frame)
 		}
 		res = my_dahdi_write(p, (unsigned char *)frame->data.ptr, frame->datalen, idx, 0);
 	} else {
+		ast_mutex_unlock(&p->lock);
 		ast_log(LOG_WARNING, "Cannot handle frames in %s format\n",
 			ast_format_get_name(frame->subclass.format));
 		return -1;
 	}
+	ast_mutex_unlock(&p->lock);
 	if (res < 0) {
 		ast_log(LOG_WARNING, "write failed: %s\n", strerror(errno));
 		return -1;
@@ -12335,6 +12366,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
 #if defined(HAVE_PRI_MCID)
 						pris[span].pri.mcid_send = conf->pri.pri.mcid_send;
 #endif	/* defined(HAVE_PRI_MCID) */
+						pris[span].pri.force_restart_unavailable_chans = conf->pri.pri.force_restart_unavailable_chans;
 #if defined(HAVE_PRI_DATETIME_SEND)
 						pris[span].pri.datetime_send = conf->pri.pri.datetime_send;
 #endif	/* defined(HAVE_PRI_DATETIME_SEND) */
@@ -12581,7 +12613,11 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
 
 			mailbox_specific_topic = ast_mwi_topic(tmp->mailbox);
 			if (mailbox_specific_topic) {
-				tmp->mwi_event_sub = stasis_subscribe_pool(mailbox_specific_topic, mwi_event_cb, NULL);
+				/* This module does not handle MWI in an event-based manner.  However, it
+				 * subscribes to MWI for each mailbox that is configured so that the core
+				 * knows that we care about it.  Then, chan_dahdi will get the MWI from the
+				 * event cache instead of checking the mailbox directly. */
+				tmp->mwi_event_sub = stasis_subscribe_pool(mailbox_specific_topic, stasis_subscription_cb_noop, NULL);
 			}
 		}
 #ifdef HAVE_DAHDI_LINEREVERSE_VMWI
@@ -12786,6 +12822,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
 				analog_p->transfer = conf->chan.transfer;
 				analog_p->transfertobusy = conf->chan.transfertobusy;
 				analog_p->use_callerid = tmp->use_callerid;
+				analog_p->usedistinctiveringdetection = tmp->usedistinctiveringdetection;
 				analog_p->use_smdi = tmp->use_smdi;
 				analog_p->smdi_iface = tmp->smdi_iface;
 				analog_p->outsigmod = ANALOG_SIG_NONE;
@@ -16066,7 +16103,7 @@ static int action_dahdishowchannels(struct mansession *s, const struct message *
 	struct dahdi_pvt *tmp = NULL;
 	const char *id = astman_get_header(m, "ActionID");
 	const char *dahdichannel = astman_get_header(m, "DAHDIChannel");
-	char idText[256] = "";
+	char idText[256];
 	int channels = 0;
 	int dahdichanquery;
 
@@ -16075,9 +16112,12 @@ static int action_dahdishowchannels(struct mansession *s, const struct message *
 		dahdichanquery = -1;
 	}
 
-	astman_send_ack(s, m, "DAHDI channel status will follow");
-	if (!ast_strlen_zero(id))
+	idText[0] = '\0';
+	if (!ast_strlen_zero(id)) {
 		snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
+	}
+
+	astman_send_listack(s, m, "DAHDI channel status will follow", "start");
 
 	ast_mutex_lock(&iflock);
 
@@ -16140,13 +16180,9 @@ static int action_dahdishowchannels(struct mansession *s, const struct message *
 
 	ast_mutex_unlock(&iflock);
 
-	astman_append(s,
-		"Event: DAHDIShowChannelsComplete\r\n"
-		"%s"
-		"Items: %d\r\n"
-		"\r\n",
-		idText,
-		channels);
+	astman_send_list_complete_start(s, m, "DAHDIShowChannelsComplete", channels);
+	astman_append(s, "Items: %d\r\n", channels);
+	astman_send_list_complete_end(s);
 	return 0;
 }
 
@@ -16175,7 +16211,7 @@ static int action_prishowspans(struct mansession *s, const struct message *m)
 		action_id[0] = '\0';
 	}
 
-	astman_send_ack(s, m, "Span status will follow");
+	astman_send_listack(s, m, "Span status will follow", "start");
 
 	count = 0;
 	for (idx = 0; idx < ARRAY_LEN(pris); ++idx) {
@@ -16192,14 +16228,9 @@ static int action_prishowspans(struct mansession *s, const struct message *m)
 		}
 	}
 
-	astman_append(s,
-		"Event: %sComplete\r\n"
-		"Items: %d\r\n"
-		"%s"
-		"\r\n",
-		show_cmd,
-		count,
-		action_id);
+	astman_send_list_complete_start(s, m, "PRIShowSpansComplete", count);
+	astman_append(s, "Items: %d\r\n", count);
+	astman_send_list_complete_end(s);
 	return 0;
 }
 #endif	/* defined(HAVE_PRI) */
@@ -18252,6 +18283,8 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
 				else
 					ast_log(LOG_WARNING, "'%s' is not a valid reset interval, should be >= 60 seconds or 'never' at line %d.\n",
 						v->value, v->lineno);
+			} else if (!strcasecmp(v->name, "force_restart_unavailable_chans")) {
+				confp->pri.pri.force_restart_unavailable_chans = ast_true(v->value);
 			} else if (!strcasecmp(v->name, "minunused")) {
 				confp->pri.pri.minunused = atoi(v->value);
 			} else if (!strcasecmp(v->name, "minidle")) {
@@ -18800,6 +18833,11 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
 					cadence_is_ok = 0;
 				}
 
+				/* This check is only needed to satisfy the compiler that element_count can't cause an out of bounds */
+				if (element_count >= ARRAY_LEN(c)) {
+					element_count = ARRAY_LEN(c) - 1;
+				}
+
 				/* Ring cadences cannot be negative */
 				for (i = 0; i < element_count; i++) {
 					if (c[i] == 0) {
@@ -18925,12 +18963,6 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
 			ast_log(LOG_WARNING, "Ignoring any changes to '%s' (on reload) at line %d.\n", v->name, v->lineno);
 	}
 
-	/* Since confp has already filled invidual dahdi_pvt objects with channels at this point, clear the variables in confp's pvt. */
-	if (confp->chan.vars) {
-		ast_variables_destroy(confp->chan.vars);
-		confp->chan.vars = NULL;
-	}
-
 	if (dahdichan) {
 		/* Process the deferred dahdichan value. */
 		if (build_channels(confp, dahdichan->value, reload, dahdichan->lineno)) {
@@ -18944,6 +18976,15 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
 		}
 	}
 
+	/*
+	 * Since confp has already filled individual dahdi_pvt objects with channels
+	 * at this point, clear the variables in confp's pvt.
+	 */
+	if (confp->chan.vars) {
+		ast_variables_destroy(confp->chan.vars);
+		confp->chan.vars = NULL;
+	}
+
 	/* mark the first channels of each DAHDI span to watch for their span alarms */
 	for (tmp = iflist, y=-1; tmp; tmp = tmp->next) {
 		if (!tmp->destroy && tmp->span != y) {
diff --git a/channels/chan_dahdi.h b/channels/chan_dahdi.h
index e4a689a..4bb5d19 100644
--- a/channels/chan_dahdi.h
+++ b/channels/chan_dahdi.h
@@ -447,7 +447,7 @@ struct dahdi_pvt {
 	 */
 	char description[32];
 	/*!
-	 * \brief Saved context string.
+	 * \brief Default distinctive ring context.
 	 */
 	char defcontext[AST_MAX_CONTEXT];
 	/*! \brief Extension to use in the dialplan. */
diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c
index aa76b75..8d5018d 100644
--- a/channels/chan_iax2.c
+++ b/channels/chan_iax2.c
@@ -58,7 +58,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428687 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/mman.h>
 #include <dirent.h>
@@ -387,7 +387,7 @@ static int (*iax2_regfunk)(const char *username, int onoff) = NULL;
 			break; \
 		\
 		for (idx = 0; idx < 16; idx++) \
-			sprintf(digest + (idx << 1), "%2.2x", (unsigned) key[idx]); \
+			sprintf(digest + (idx << 1), "%02hhx", (unsigned char) key[idx]); \
 		\
 		ast_log(LOG_NOTICE, msg " IAX_COMMAND_RTKEY to rotate key to '%s'\n", digest); \
 	} while(0)
@@ -395,8 +395,6 @@ static int (*iax2_regfunk)(const char *username, int onoff) = NULL;
 static struct io_context *io;
 static struct ast_sched_context *sched;
 
-#define DONT_RESCHEDULE -2
-
 static iax2_format iax2_capability = IAX_CAPABILITY_FULLBANDWIDTH;
 
 static int iaxdebug = 0;
@@ -455,7 +453,6 @@ struct iax2_context {
 #define IAX_RTCACHEFRIENDS      (uint64_t)(1 << 17)   /*!< let realtime stay till your reload */
 #define IAX_RTUPDATE            (uint64_t)(1 << 18)   /*!< Send a realtime update */
 #define IAX_RTAUTOCLEAR         (uint64_t)(1 << 19)   /*!< erase me on expire */
-#define IAX_FORCEJITTERBUF      (uint64_t)(1 << 20)   /*!< Force jitterbuffer, even when bridged to a channel that can take jitter */
 #define IAX_RTIGNOREREGEXPIRE   (uint64_t)(1 << 21)   /*!< When using realtime, ignore registration expiration */
 #define IAX_TRUNKTIMESTAMPS     (uint64_t)(1 << 22)   /*!< Send trunk timestamps */
 #define IAX_TRANSFERMEDIA       (uint64_t)(1 << 23)   /*!< When doing IAX2 transfers, transfer media only */
@@ -865,6 +862,8 @@ struct chan_iax2_pvt {
 	int frames_dropped;
 	/*! received frame count: (just for stats) */
 	int frames_received;
+	/*! Destroying this call initiated. */
+	int destroy_initiated;
 	/*! num bytes used for calltoken ie, even an empty ie should contain 2 */
 	unsigned char calltoken_ie_len;
 	/*! hold all signaling frames from the pbx thread until we have a destination callno */
@@ -1439,13 +1438,6 @@ static int iax2_is_control_frame_allowed(int subtype)
 	return is_allowed;
 }
 
-static void mwi_event_cb(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
-{
-	/* The MWI subscriptions exist just so the core knows we care about those
-	 * mailboxes.  However, we just grab the events out of the cache when it
-	 * is time to send MWI, since it is only sent with a REGACK. */
-}
-
 static void network_change_stasis_subscribe(void)
 {
 	if (!network_change_sub) {
@@ -1680,23 +1672,48 @@ static int iax2_sched_add(struct ast_sched_context *con, int when,
 	return ast_sched_add(con, when, callback, data);
 }
 
+/*
+ * \brief Acquire the iaxsl[callno] if call exists and not having ongoing hangup.
+ * \param callno Call number to lock.
+ * \return 0 If call disappeared or has ongoing hangup procedure. 1 If call found and mutex is locked.
+ */
+static int iax2_lock_callno_unless_destroyed(int callno)
+{
+	ast_mutex_lock(&iaxsl[callno]);
+
+	/* We acquired the lock; but the call was already destroyed (we came after full hang up procedures)
+	 * or destroy initiated (in middle of hang up procedure. */
+	if (!iaxs[callno] || iaxs[callno]->destroy_initiated) {
+		ast_debug(3, "I wanted to lock callno %d, but it is dead or going to die.\n", callno);
+		ast_mutex_unlock(&iaxsl[callno]);
+		return 0;
+	}
+
+	/* Lock acquired, and callno is alive and kicking. */
+	return 1;
+}
+
 static int send_ping(const void *data);
 
 static void __send_ping(const void *data)
 {
-	int callno = (long) data;
+	int callno = PTR_TO_CALLNO(data);
 
-	ast_mutex_lock(&iaxsl[callno]);
+	if (iax2_lock_callno_unless_destroyed(callno) == 0) {
+		ast_debug(3, "Hangup initiated on call %d, aborting __send_ping\n", callno);
+		return;
+	}
 
-	if (iaxs[callno]) {
-		if (iaxs[callno]->peercallno) {
-			send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_PING, 0, NULL, 0, -1);
-			if (iaxs[callno]->pingid != DONT_RESCHEDULE) {
-				iaxs[callno]->pingid = iax2_sched_add(sched, ping_time * 1000, send_ping, data);
-			}
-		}
-	} else {
-		ast_debug(1, "I was supposed to send a PING with callno %d, but no such call exists.\n", callno);
+	/* Mark pingid as invalid scheduler id. */
+	iaxs[callno]->pingid = -1;
+
+	/* callno is now locked. */
+	if (iaxs[callno]->peercallno) {
+		/* Send PING packet. */
+		send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_PING, 0, NULL, 0, -1);
+
+		/* Schedule sending next ping. */
+		iaxs[callno]->pingid = iax2_sched_add(sched, ping_time * 1000, send_ping, data);
 	}
 
 	ast_mutex_unlock(&iaxsl[callno]);
@@ -1704,13 +1721,6 @@ static void __send_ping(const void *data)
 
 static int send_ping(const void *data)
 {
-	int callno = (long) data;
-	ast_mutex_lock(&iaxsl[callno]);
-	if (iaxs[callno] && iaxs[callno]->pingid != DONT_RESCHEDULE) {
-		iaxs[callno]->pingid = -1;
-	}
-	ast_mutex_unlock(&iaxsl[callno]);
-
 #ifdef SCHED_MULTITHREADED
 	if (schedule_action(__send_ping, data))
 #endif
@@ -1751,19 +1761,23 @@ static int send_lagrq(const void *data);
 
 static void __send_lagrq(const void *data)
 {
-	int callno = (long) data;
+	int callno = PTR_TO_CALLNO(data);
 
-	ast_mutex_lock(&iaxsl[callno]);
+	if (iax2_lock_callno_unless_destroyed(callno) == 0) {
+		ast_debug(3, "Hangup initiated on call %d, aborting __send_lagrq\n", callno);
+		return;
+	}
 
-	if (iaxs[callno]) {
-		if (iaxs[callno]->peercallno) {
-			send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_LAGRQ, 0, NULL, 0, -1);
-			if (iaxs[callno]->lagid != DONT_RESCHEDULE) {
-				iaxs[callno]->lagid = iax2_sched_add(sched, lagrq_time * 1000, send_lagrq, data);
-			}
-		}
-	} else {
-		ast_debug(1, "I was supposed to send a LAGRQ with callno %d, but no such call exists.\n", callno);
+	/* Mark lagid as invalid scheduler id. */
+	iaxs[callno]->lagid = -1;
+
+	/* callno is now locked. */
+	if (iaxs[callno]->peercallno) {
+		/* Send LAGRQ packet. */
+		send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_LAGRQ, 0, NULL, 0, -1);
+
+		/* Schedule sending next lagrq. */
+		iaxs[callno]->lagid = iax2_sched_add(sched, lagrq_time * 1000, send_lagrq, data);
 	}
 
 	ast_mutex_unlock(&iaxsl[callno]);
@@ -1771,13 +1785,6 @@ static void __send_lagrq(const void *data)
 
 static int send_lagrq(const void *data)
 {
-	int callno = (long) data;
-	ast_mutex_lock(&iaxsl[callno]);
-	if (iaxs[callno] && iaxs[callno]->lagid != DONT_RESCHEDULE) {
-		iaxs[callno]->lagid = -1;
-	}
-	ast_mutex_unlock(&iaxsl[callno]);
-
 #ifdef SCHED_MULTITHREADED
 	if (schedule_action(__send_lagrq, data))
 #endif
@@ -2023,11 +2030,6 @@ static struct iax2_user *find_user(const char *name)
 {
 	return ao2_find(users, name, OBJ_KEY);
 }
-static inline struct iax2_user *user_ref(struct iax2_user *user)
-{
-	ao2_ref(user, +1);
-	return user;
-}
 
 static inline struct iax2_user *user_unref(struct iax2_user *user)
 {
@@ -2066,6 +2068,16 @@ static int iax2_getpeername(struct ast_sockaddr addr, char *host, int len)
 	return res;
 }
 
+/* Call AST_SCHED_DEL on a scheduled task if it is found in scheduler. */
+static int iax2_delete_from_sched(const void* data)
+{
+	int sched_id = (int)(long)data;
+
+	AST_SCHED_DEL(sched, sched_id);
+
+	return 0;
+}
+
 /*!\note Assumes the lock on the pvt is already held, when
  * iax2_destroy_helper() is called. */
 static void iax2_destroy_helper(struct chan_iax2_pvt *pvt)
@@ -2082,11 +2094,27 @@ static void iax2_destroy_helper(struct chan_iax2_pvt *pvt)
 
 		ast_clear_flag64(pvt, IAX_MAXAUTHREQ);
 	}
-	/* No more pings or lagrq's */
-	AST_SCHED_DEL_SPINLOCK(sched, pvt->pingid, &iaxsl[pvt->callno]);
-	pvt->pingid = DONT_RESCHEDULE;
-	AST_SCHED_DEL_SPINLOCK(sched, pvt->lagid, &iaxsl[pvt->callno]);
-	pvt->lagid = DONT_RESCHEDULE;
+
+
+	/* Mark call destroy initiated flag. */
+	pvt->destroy_initiated = 1;
+
+	/*
+	 * Schedule deleting the scheduled (but didn't run yet) PINGs or LAGRQs.
+	 * Already running tasks will be terminated because of destroy_initiated.
+	 *
+	 * Don't call AST_SCHED_DEL from this thread for pingid and lagid because
+	 * it leads to a deadlock between the scheduler thread callback locking
+	 * the callno mutex and this thread which holds the callno mutex one or
+	 * more times.  It is better to have another thread delete the scheduled
+	 * callbacks which doesn't lock the callno mutex.
+	 */
+	iax2_sched_add(sched, 0, iax2_delete_from_sched, (void*)(long)pvt->pingid);
+	iax2_sched_add(sched, 0, iax2_delete_from_sched, (void*)(long)pvt->lagid);
+
+	pvt->pingid = -1;
+	pvt->lagid = -1;
+
 	AST_SCHED_DEL(sched, pvt->autoid);
 	AST_SCHED_DEL(sched, pvt->authid);
 	AST_SCHED_DEL(sched, pvt->initid);
@@ -2221,6 +2249,14 @@ static struct chan_iax2_pvt *new_iax(struct ast_sockaddr *addr, const char *host
 		return NULL;
 	}
 
+	tmp->pingid = -1;
+	tmp->lagid = -1;
+	tmp->autoid = -1;
+	tmp->authid = -1;
+	tmp->initid = -1;
+	tmp->keyrotateid = -1;
+	tmp->jbid = -1;
+
 	if (ast_string_field_init(tmp, 32)) {
 		ao2_ref(tmp, -1);
 		tmp = NULL;
@@ -2228,18 +2264,11 @@ static struct chan_iax2_pvt *new_iax(struct ast_sockaddr *addr, const char *host
 	}
 
 	tmp->prefs = prefs_global;
-	tmp->pingid = -1;
-	tmp->lagid = -1;
-	tmp->autoid = -1;
-	tmp->authid = -1;
-	tmp->initid = -1;
-	tmp->keyrotateid = -1;
 
 	ast_string_field_set(tmp,exten, "s");
 	ast_string_field_set(tmp,host, host);
 
 	tmp->jb = jb_new();
-	tmp->jbid = -1;
 	jbconf.max_jitterbuf = maxjitterbuffer;
 	jbconf.resync_threshold = resyncthreshold;
 	jbconf.max_contig_interp = maxjitterinterps;
@@ -3180,7 +3209,7 @@ static int __find_callno(unsigned short callno, unsigned short dcallno, struct a
 			iaxs[x]->pingid = iax2_sched_add(sched, ping_time * 1000, send_ping, (void *)(long)x);
 			iaxs[x]->lagid = iax2_sched_add(sched, lagrq_time * 1000, send_lagrq, (void *)(long)x);
 			iaxs[x]->amaflags = amaflags;
-			ast_copy_flags64(iaxs[x], &globalflags, IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE | IAX_FORCE_ENCRYPT);
+			ast_copy_flags64(iaxs[x], &globalflags, IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE | IAX_FORCE_ENCRYPT);
 			ast_string_field_set(iaxs[x], accountcode, accountcode);
 			ast_string_field_set(iaxs[x], mohinterpret, mohinterpret);
 			ast_string_field_set(iaxs[x], mohsuggest, mohsuggest);
@@ -3788,7 +3817,7 @@ static char *handle_cli_iax2_show_peer(struct ast_cli_entry *e, int cmd, struct
 	char status[30];
 	char cbuf[256];
 	struct iax2_peer *peer;
-	struct ast_str *codec_buf = ast_str_alloca(256);
+	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 	struct ast_str *encmethods = ast_str_alloca(256);
 	int load_realtime = 0;
 
@@ -4191,8 +4220,6 @@ static int schedule_delivery(struct iax_frame *fr, int updatehistory, int fromtr
 	int type, len;
 	int ret;
 	int needfree = 0;
-	struct ast_channel *owner = NULL;
-	RAII_VAR(struct ast_channel *, bridge, NULL, ast_channel_cleanup);
 
 	/*
 	 * Clear fr->af.data if there is no data in the buffer.  Things
@@ -4233,45 +4260,6 @@ static int schedule_delivery(struct iax_frame *fr, int updatehistory, int fromtr
 		return -1;
 	}
 
-	iax2_lock_owner(fr->callno);
-	if (!iaxs[fr->callno]) {
-		/* The call dissappeared so discard this frame that we could not send. */
-		iax2_frame_free(fr);
-		return -1;
-	}
-	if ((owner = iaxs[fr->callno]->owner)) {
-		bridge = ast_channel_bridge_peer(owner);
-	}
-
-	/* if the user hasn't requested we force the use of the jitterbuffer, and we're bridged to
-	 * a channel that can accept jitter, then flush and suspend the jb, and send this frame straight through */
-	if ( (!ast_test_flag64(iaxs[fr->callno], IAX_FORCEJITTERBUF)) && owner && bridge && (ast_channel_tech(bridge)->properties & AST_CHAN_TP_WANTSJITTER) ) {
-		jb_frame frame;
-
-		ast_channel_unlock(owner);
-
-		/* deliver any frames in the jb */
-		while (jb_getall(iaxs[fr->callno]->jb, &frame) == JB_OK) {
-			__do_deliver(frame.data);
-			/* __do_deliver() can make the call disappear */
-			if (!iaxs[fr->callno])
-				return -1;
-		}
-
-		jb_reset(iaxs[fr->callno]->jb);
-
-		AST_SCHED_DEL(sched, iaxs[fr->callno]->jbid);
-
-		/* deliver this frame now */
-		if (tsout)
-			*tsout = fr->ts;
-		__do_deliver(fr);
-		return -1;
-	}
-	if (owner) {
-		ast_channel_unlock(owner);
-	}
-
 	/* insert into jitterbuffer */
 	/* TODO: Perhaps we could act immediately if it's not droppable and late */
 	ret = jb_put(iaxs[fr->callno]->jb, fr, type, len, fr->ts,
@@ -4406,7 +4394,7 @@ static struct iax2_peer *realtime_peer(const char *peername, struct ast_sockaddr
 		if (var && !ast_sockaddr_isnull(addr)) {
 			for (tmp = var; tmp; tmp = tmp->next) {
 				if (!strcasecmp(tmp->name, "host")) {
-					struct ast_sockaddr *hostaddr;
+					struct ast_sockaddr *hostaddr = NULL;
 
 					if (!ast_sockaddr_resolve(&hostaddr, tmp->value, PARSE_PORT_FORBID, AST_AF_UNSPEC)
 						|| ast_sockaddr_cmp_addr(hostaddr, addr)) {
@@ -4414,6 +4402,7 @@ static struct iax2_peer *realtime_peer(const char *peername, struct ast_sockaddr
 						ast_variables_destroy(var);
 						var = NULL;
 					}
+					ast_free(hostaddr);
 					break;
 				}
 			}
@@ -4528,7 +4517,7 @@ static struct iax2_user *realtime_user(const char *username, struct ast_sockaddr
 		if (var) {
 			for (tmp = var; tmp; tmp = tmp->next) {
 				if (!strcasecmp(tmp->name, "host")) {
-					struct ast_sockaddr *hostaddr;
+					struct ast_sockaddr *hostaddr = NULL;
 
 					if (!ast_sockaddr_resolve(&hostaddr, tmp->value, PARSE_PORT_FORBID, AST_AF_UNSPEC)
 						|| ast_sockaddr_cmp_addr(hostaddr, addr)) {
@@ -4536,6 +4525,7 @@ static struct iax2_user *realtime_user(const char *username, struct ast_sockaddr
 						ast_variables_destroy(var);
 						var = NULL;
 					}
+					ast_free(hostaddr);
 					break;
 				}
 			}
@@ -4670,7 +4660,7 @@ static int create_addr(const char *peername, struct ast_channel *c, struct ast_s
 	if (peer->maxms && ((peer->lastms > peer->maxms) || (peer->lastms < 0)))
 		goto return_unref;
 
-	ast_copy_flags64(cai, peer, IAX_SENDANI | IAX_TRUNK | IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE | IAX_FORCE_ENCRYPT);
+	ast_copy_flags64(cai, peer, IAX_SENDANI | IAX_TRUNK | IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE | IAX_FORCE_ENCRYPT);
 	cai->maxtime = peer->maxms;
 	cai->capability = peer->capability;
 	cai->encmethods = peer->encmethods;
@@ -5600,8 +5590,8 @@ static enum ast_bridge_result iax2_bridge(struct ast_channel *c0, struct ast_cha
 			return AST_BRIDGE_FAILED_NOWARN;
 		}
 		if (!(ast_format_cap_identical(ast_channel_nativeformats(c0), ast_channel_nativeformats(c1)))) {
-			struct ast_str *c0_buf = ast_str_alloca(64);
-			struct ast_str *c1_buf = ast_str_alloca(64);
+			struct ast_str *c0_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+			struct ast_str *c1_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 			ast_verb(3, "Operating with different codecs [%s] [%s] , can't native bridge...\n",
 				ast_format_cap_get_names(ast_channel_nativeformats(c0), &c0_buf),
@@ -6044,7 +6034,7 @@ static unsigned int calc_txpeerstamp(struct iax2_trunk_peer *tpeer, int sampms,
 	ms = ast_tvdiff_ms(*now, tpeer->txtrunktime);
 	/* Predict from last value */
 	pred = tpeer->lastsent + sampms;
-	if (abs(ms - pred) < MAX_TIMESTAMP_SKEW)
+	if (labs(ms - pred) < MAX_TIMESTAMP_SKEW)
 		ms = pred;
 
 	/* We never send the same timestamp twice, so fudge a little if we must */
@@ -6117,7 +6107,8 @@ static unsigned int calc_timestamp(struct chan_iax2_pvt *p, unsigned int ts, str
 			ms = 0;
 		if (voice) {
 			/* On a voice frame, use predicted values if appropriate */
-			if (p->notsilenttx && abs(ms - p->nextpred) <= MAX_TIMESTAMP_SKEW) {
+			adjust = (ms - p->nextpred);
+			if (p->notsilenttx && abs(adjust) <= MAX_TIMESTAMP_SKEW) {
 				/* Adjust our txcore, keeping voice and non-voice synchronized */
 				/* AN EXPLANATION:
 				   When we send voice, we usually send "calculated" timestamps worked out
@@ -6136,7 +6127,6 @@ static unsigned int calc_timestamp(struct chan_iax2_pvt *p, unsigned int ts, str
 				   changing at all.  But if a consistent different starts to develop it
 				   will be eliminated over the course of 10 frames (200-300msecs)
 				*/
-				adjust = (ms - p->nextpred);
 				if (adjust < 0)
 					p->offset = ast_tvsub(p->offset, ast_samp2tv(abs(adjust), 10000));
 				else if (adjust > 0)
@@ -6158,9 +6148,9 @@ static unsigned int calc_timestamp(struct chan_iax2_pvt *p, unsigned int ts, str
 				* silent periods are multiples of
 				* frame size too) */
 
-				if (iaxdebug && abs(ms - p->nextpred) > MAX_TIMESTAMP_SKEW )
+				if (iaxdebug && abs(adjust) > MAX_TIMESTAMP_SKEW )
 					ast_debug(1, "predicted timestamp skew (%d) > max (%d), using real ts instead.\n",
-						abs(ms - p->nextpred), MAX_TIMESTAMP_SKEW);
+						abs(adjust), MAX_TIMESTAMP_SKEW);
 
 				if (f->samples >= rate) /* check to make sure we don't core dump */
 				{
@@ -6186,11 +6176,12 @@ static unsigned int calc_timestamp(struct chan_iax2_pvt *p, unsigned int ts, str
 		} else {
 			/* On a dataframe, use last value + 3 (to accomodate jitter buffer shrinking) if appropriate unless
 			   it's a genuine frame */
+			adjust = (ms - p->lastsent);
 			if (genuine) {
 				/* genuine (IAX LAGRQ etc) must keep their clock-based stamps */
 				if (ms <= p->lastsent)
 					ms = p->lastsent + 3;
-			} else if (abs(ms - p->lastsent) <= MAX_TIMESTAMP_SKEW) {
+			} else if (abs(adjust) <= MAX_TIMESTAMP_SKEW) {
 				/* non-genuine frames (!?) (DTMF, CONTROL) should be pulled into the predicted stream stamps */
 				ms = p->lastsent + 3;
 			}
@@ -6442,7 +6433,7 @@ static int decode_frame(ast_aes_decrypt_key *dcx, struct ast_iax2_full_hdr *fh,
 
 		padding = 16 + (workspace[15] & 0x0f);
 		if (iaxdebug)
-			ast_debug(1, "Decoding full frame with length %d (padding = %d) (15=%02x)\n", *datalen, padding, (unsigned)workspace[15]);
+			ast_debug(1, "Decoding full frame with length %d (padding = %d) (15=%02hhx)\n", *datalen, padding, workspace[15]);
 		if (*datalen < padding + sizeof(struct ast_iax2_full_hdr))
 			return -1;
 
@@ -6489,7 +6480,7 @@ static int encrypt_frame(ast_aes_encrypt_key *ecx, struct ast_iax2_full_hdr *fh,
 		workspace[15] &= 0xf0;
 		workspace[15] |= (padding & 0xf);
 		if (iaxdebug)
-			ast_debug(1, "Encoding full frame %d/%d with length %d + %d padding (15=%02x)\n", fh->type, fh->csub, *datalen, padding, (unsigned)workspace[15]);
+			ast_debug(1, "Encoding full frame %d/%d with length %d + %d padding (15=%02hhx)\n", fh->type, fh->csub, *datalen, padding, workspace[15]);
 		*datalen += padding;
 		memcpy_encrypt(efh->encdata, workspace, *datalen - sizeof(struct ast_iax2_full_enc_hdr), ecx);
 		if (*datalen >= 32 + sizeof(struct ast_iax2_full_enc_hdr))
@@ -6872,31 +6863,23 @@ static void _iax2_show_peers_one(int fd, struct mansession *s, struct show_peers
 	}
 
 	if (s) {
-
 		if (cont->peerlist) { /* IAXpeerlist */
-
 			astman_append(s,
 				"Event: PeerEntry\r\n%s"
 				"Channeltype: IAX\r\n",
 				cont->idtext);
-
 			if (!ast_strlen_zero(peer->username)) {
-
 				astman_append(s,
 					"ObjectName: %s\r\n"
 					"ObjectUsername: %s\r\n",
 					peer->name,
 					peer->username);
-
 			} else {
-
 				astman_append(s,
 					"ObjectName: %s\r\n",
 					name);
 			}
-
 		} else { /* IAXpeers */
-
 			astman_append(s,
 				"Event: PeerEntry\r\n%s"
 				"Channeltype: IAX2\r\n"
@@ -6904,28 +6887,21 @@ static void _iax2_show_peers_one(int fd, struct mansession *s, struct show_peers
 				cont->idtext,
 				name);
 		}
-
 		astman_append(s,
 			"ChanObjectType: peer\r\n"
 			"IPaddress: %s\r\n",
 			tmp_host);
-
 		if (cont->peerlist) { /* IAXpeerlist */
-
 			astman_append(s,
 				"Mask: %s\r\n"
 				"Port: %s\r\n",
 				tmp_mask,
 				tmp_port);
-
 		} else { /* IAXpeers */
-
 			astman_append(s,
 				"IPport: %s\r\n",
 				tmp_port);
-
 		}
-
 		astman_append(s,
 			"Dynamic: %s\r\n"
 			"Trunk: %s\r\n"
@@ -6935,19 +6911,13 @@ static void _iax2_show_peers_one(int fd, struct mansession *s, struct show_peers
 			ast_test_flag64(peer, IAX_TRUNK) ? "yes" : "no",
 			peer->encmethods ? ast_str_buffer(encmethods) : "no",
 			status);
-
 		if (cont->peerlist) { /* IAXpeerlist */
-
 			astman_append(s, "\r\n");
-
 		} else { /* IAXpeers */
-
 			astman_append(s,
 				"Description: %s\r\n\r\n",
 				peer->description);
-
 		}
-
 	} else {
 		ast_cli(fd, PEERS_FORMAT,
 			name,
@@ -6962,7 +6932,6 @@ static void _iax2_show_peers_one(int fd, struct mansession *s, struct show_peers
 	}
 
 	cont->total_peers++;
-
 }
 
 static int __iax2_show_peers(int fd, int *total, struct mansession *s, const int argc, const char * const argv[])
@@ -7146,7 +7115,7 @@ static char *handle_cli_iax2_unregister(struct ast_cli_entry *e, int cmd, struct
 
 	p = find_peer(a->argv[2], 1);
 	if (p) {
-		if (p->expire > 0) {
+		if (p->expire > -1) {
 			struct iax2_peer *peer;
 
 			peer = ao2_find(peers, a->argv[2], OBJ_KEY);
@@ -7178,8 +7147,8 @@ static char *complete_iax2_unregister(const char *line, const char *word, int po
 	if (pos == 2) {
 		struct ao2_iterator i = ao2_iterator_init(peers, 0);
 		while ((p = ao2_iterator_next(&i))) {
-			if (!strncasecmp(p->name, word, wordlen) &&
-				++which > state && p->expire > 0) {
+			if (!strncasecmp(p->name, word, wordlen) && 
+				++which > state && p->expire > -1) {
 				res = ast_strdup(p->name);
 				peer_unref(p);
 				break;
@@ -7275,16 +7244,14 @@ static int manager_iax2_show_peers(struct mansession *s, const struct message *m
 		snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
 
 	astman_send_listack(s, m, "Peer status list will follow", "start");
-        /* List the peers in separate manager events */
+
+	/* List the peers in separate manager events */
 	__iax2_show_peers(-1, &total, s, 3, a);
-        /* Send final confirmation */
-        astman_append(s,
-        "Event: PeerlistComplete\r\n"
-        "EventList: Complete\r\n"
-        "ListItems: %d\r\n"
-        "%s"
-        "\r\n", total, idtext);
-        return 0;
+
+	/* Send final confirmation */
+	astman_send_list_complete_start(s, m, "PeerlistComplete", total);
+	astman_send_list_complete_end(s);
+	return 0;
 }
 
 /*! \brief callback to display iax peers in manager format */
@@ -7312,25 +7279,16 @@ static int manager_iax2_show_peer_list(struct mansession *s, const struct messag
 		snprintf(cont.idtext, sizeof(cont.idtext), "ActionID: %s\r\n", id);
 	}
 
-	astman_append(s,
-		"Response: Success\r\n"
-		"%sMessage: IAX Peer status list will follow\r\n\r\n",
-		cont.idtext);
-
+	astman_send_listack(s, m, "IAX Peer status list will follow", "start");
 
 	i = ao2_iterator_init(peers, 0);
 	for (; (peer = ao2_iterator_next(&i)); peer_unref(peer)) {
-
 		_iax2_show_peers_one(-1, s, &cont, peer);
-
 	}
 	ao2_iterator_destroy(&i);
 
-	astman_append(s,
-		"Event: PeerlistComplete\r\n"
-		"%sListItems: %d\r\n\r\n",
-		cont.idtext,
-		cont.total_peers);
+	astman_send_list_complete_start(s, m, "PeerlistComplete", cont.total_peers);
+	astman_send_list_complete_end(s);
 
 	return RESULT_SUCCESS;
 }
@@ -7435,12 +7393,8 @@ static int manager_iax2_show_registry(struct mansession *s, const struct message
 	}
 	AST_LIST_UNLOCK(&registrations);
 
-	astman_append(s,
-		"Event: RegistrationsComplete\r\n"
-		"EventList: Complete\r\n"
-		"ListItems: %d\r\n"
-		"%s"
-		"\r\n", total, idtext);
+	astman_send_list_complete_start(s, m, "RegistrationsComplete", total);
+	astman_send_list_complete_end(s);
 
 	return 0;
 }
@@ -8014,7 +7968,7 @@ static int check_access(int callno, struct ast_sockaddr *addr, struct iax_ies *i
 			iaxs[callno]->amaflags = user->amaflags;
 		if (!ast_strlen_zero(user->language))
 			ast_string_field_set(iaxs[callno], language, user->language);
-		ast_copy_flags64(iaxs[callno], user, IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE);
+		ast_copy_flags64(iaxs[callno], user, IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE);
 		/* Keep this check last */
 		if (!ast_strlen_zero(user->dbsecret)) {
 			char *family, *key=NULL;
@@ -8207,7 +8161,7 @@ static int authenticate_verify(struct chan_iax2_pvt *p, struct iax_ies *ies)
 			MD5Final(digest, &md5);
 			/* If they support md5, authenticate with it.  */
 			for (x=0;x<16;x++)
-				sprintf(requeststr + (x << 1), "%2.2x", (unsigned)digest[x]); /* safe */
+				sprintf(requeststr + (x << 1), "%02hhx", digest[x]); /* safe */
 			if (!strcasecmp(requeststr, md5secret)) {
 				res = 0;
 				break;
@@ -8339,7 +8293,7 @@ static int register_verify(int callno, struct ast_sockaddr *addr, struct iax_ies
 			MD5Update(&md5, (unsigned char *)tmppw, strlen(tmppw));
 			MD5Final(digest, &md5);
 			for (x=0;x<16;x++)
-				sprintf(requeststr + (x << 1), "%2.2x", (unsigned)digest[x]); /* safe */
+				sprintf(requeststr + (x << 1), "%02hhx", digest[x]); /* safe */
 			if (!strcasecmp(requeststr, md5secret))
 				break;
 		}
@@ -8423,7 +8377,7 @@ static int authenticate(const char *challenge, const char *secret, const char *k
 			MD5Final(digest, &md5);
 			/* If they support md5, authenticate with it.  */
 			for (x=0;x<16;x++)
-				sprintf(digres + (x << 1),  "%2.2x", (unsigned)digest[x]); /* safe */
+				sprintf(digres + (x << 1),  "%02hhx", digest[x]); /* safe */
 			if (pvt) {
 				build_encryption_keys(digest, pvt);
 			}
@@ -10862,9 +10816,9 @@ static int socket_process_helper(struct iax2_thread *thread)
 									break;
 								}
 								if (authdebug) {
-									struct ast_str *peer_buf = ast_str_alloca(64);
-									struct ast_str *cap_buf = ast_str_alloca(64);
-									struct ast_str *peer_form_buf = ast_str_alloca(64);
+									struct ast_str *peer_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+									struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+									struct ast_str *peer_form_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 									if (ast_test_flag64(iaxs[fr->callno], IAX_CODEC_NOCAP)) {
 										ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested '%s' incompatible with our capability '%s'.\n",
@@ -10909,9 +10863,9 @@ static int socket_process_helper(struct iax2_thread *thread)
 								}
 
 								if (!format) {
-									struct ast_str *peer_buf = ast_str_alloca(64);
-									struct ast_str *cap_buf = ast_str_alloca(64);
-									struct ast_str *peer_form_buf = ast_str_alloca(64);
+									struct ast_str *peer_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+									struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+									struct ast_str *peer_form_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 									memset(&ied0, 0, sizeof(ied0));
 									iax_ie_append_str(&ied0, IAX_IE_CAUSE, "Unable to negotiate codec");
@@ -11089,8 +11043,8 @@ static int socket_process_helper(struct iax2_thread *thread)
 						break;
 					}
 					if (authdebug) {
-						struct ast_str *peer_buf = ast_str_alloca(64);
-						struct ast_str *cap_buf = ast_str_alloca(64);
+						struct ast_str *peer_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+						struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 						ast_log(LOG_NOTICE, "Rejected call to %s, format %s incompatible with our capability %s.\n",
 							ast_sockaddr_stringify(&addr),
@@ -11103,7 +11057,7 @@ static int socket_process_helper(struct iax2_thread *thread)
 					ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED);
 					iax2_lock_owner(fr->callno);
 					if (iaxs[fr->callno] && iaxs[fr->callno]->owner && native) {
-						struct ast_str *cap_buf = ast_str_alloca(64);
+						struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 						/* Switch us to use a compatible format */
 						iax2_codec_pref_best_bitfield2cap(
@@ -11307,9 +11261,9 @@ static int socket_process_helper(struct iax2_thread *thread)
 						iax2_codec_pref_string(&iaxs[fr->callno]->prefs, host_pref_buf, sizeof(host_pref_buf) - 1);
 					}
 					if (!format) {
-						struct ast_str *cap_buf = ast_str_alloca(64);
-						struct ast_str *peer_buf = ast_str_alloca(64);
-						struct ast_str *peer_form_buf = ast_str_alloca(64);
+						struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+						struct ast_str *peer_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+						struct ast_str *peer_form_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 						if(!ast_test_flag64(iaxs[fr->callno], IAX_CODEC_NOCAP)) {
 							ast_debug(1, "We don't do requested format %s, falling back to peer capability '%s'\n",
@@ -11370,9 +11324,9 @@ static int socket_process_helper(struct iax2_thread *thread)
 								}
 							}
 							if (!format) {
-								struct ast_str *cap_buf = ast_str_alloca(64);
-								struct ast_str *peer_buf = ast_str_alloca(64);
-								struct ast_str *peer_form_buf = ast_str_alloca(64);
+								struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+								struct ast_str *peer_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+								struct ast_str *peer_form_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 								ast_log(LOG_ERROR, "No best format in %s???\n",
 									iax2_getformatname_multiple(iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability, &cap_buf));
@@ -11496,7 +11450,7 @@ immediatedial:
 							break;
 						}
 					} else {
-						struct ast_str *cap_buf = ast_str_alloca(64);
+						struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 						ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED);
 						ast_verb(3, "Accepting DIAL from %s, formats = %s\n",
 								ast_sockaddr_stringify(&addr),
@@ -11972,7 +11926,7 @@ immediatedial:
 		if (iaxdebug && iaxs[fr->callno]) {
 			ast_debug(1, "Received out of order packet... (type=%u, subclass %d, ts = %u, last = %u)\n", f.frametype, f.subclass.integer, fr->ts, iaxs[fr->callno]->last);
 		}
-		fr->outoforder = -1;
+		fr->outoforder = 1;
 	}
 	fr->cacheable = ((f.frametype == AST_FRAME_VOICE) || (f.frametype == AST_FRAME_VIDEO));
 	if (iaxs[fr->callno]) {
@@ -12399,6 +12353,7 @@ static int iax2_poke_peer_cb(void *obj, void *arg, int flags)
 static int iax2_poke_peer(struct iax2_peer *peer, int heldcall)
 {
 	int callno;
+	int poke_timeout;
 
 	if (!peer->maxms || (ast_sockaddr_isnull(&peer->addr) && !peer->dnsmgr)) {
 		/* IF we have no IP without dnsmgr, or this isn't to be monitored, return
@@ -12422,15 +12377,11 @@ static int iax2_poke_peer(struct iax2_peer *peer, int heldcall)
 	callno = peer->callno = find_callno(0, 0, &peer->addr, NEW_FORCE, peer->sockfd, 0);
 	if (heldcall)
 		ast_mutex_lock(&iaxsl[heldcall]);
-	if (peer->callno < 1) {
+	if (callno < 1) {
 		ast_log(LOG_WARNING, "Unable to allocate call for poking peer '%s'\n", peer->name);
 		return -1;
 	}
 
-	/* Speed up retransmission times for this qualify call */
-	iaxs[peer->callno]->pingtime = peer->maxms / 4 + 1;
-	iaxs[peer->callno]->peerpoke = peer;
-
 	if (peer->pokeexpire > -1) {
 		if (!AST_SCHED_DEL(sched, peer->pokeexpire)) {
 			peer->pokeexpire = -1;
@@ -12438,12 +12389,24 @@ static int iax2_poke_peer(struct iax2_peer *peer, int heldcall)
 		}
 	}
 
+	if (peer->lastms < 0){
+		/* If the host is already unreachable then use time less than the unreachable
+		 * interval. 5/6 is arbitrary multiplier to get value less than
+		 * peer->pokefreqnotok. Value less than peer->pokefreqnotok is used to expire
+		 * current POKE before starting new POKE (which is scheduled after
+		 * peer->pokefreqnotok). */
+		poke_timeout = peer->pokefreqnotok * 5  / 6;
+	} else {
+		/* If the host is reachable, use timeout large enough to allow for multiple
+		 * POKE retries. Limit this value to less than peer->pokefreqok. 5/6 is arbitrary
+		 * multiplier to get value less than peer->pokefreqok. Value less than
+		 * peer->pokefreqok is used to expire current POKE before starting new POKE
+		 * (which is scheduled after peer->pokefreqok). */
+		poke_timeout = MIN(MAX_RETRY_TIME * 2 + peer->maxms, peer->pokefreqok * 5  / 6);
+	}
+
 	/* Queue up a new task to handle no reply */
-	/* If the host is already unreachable then use the unreachable interval instead */
-	if (peer->lastms < 0)
-		peer->pokeexpire = iax2_sched_add(sched, peer->pokefreqnotok, iax2_poke_noanswer, peer_ref(peer));
-	else
-		peer->pokeexpire = iax2_sched_add(sched, DEFAULT_MAXMS * 2, iax2_poke_noanswer, peer_ref(peer));
+	peer->pokeexpire = iax2_sched_add(sched, poke_timeout, iax2_poke_noanswer, peer_ref(peer));
 
 	if (peer->pokeexpire == -1)
 		peer_unref(peer);
@@ -12455,6 +12418,11 @@ static int iax2_poke_peer(struct iax2_peer *peer, int heldcall)
 			.buf = { 0 },
 			.pos = 0,
 		};
+
+		/* Speed up retransmission times for this qualify call */
+		iaxs[callno]->pingtime = peer->maxms / 8;
+		iaxs[callno]->peerpoke = peer;
+
 		add_empty_calltoken_ie(iaxs[callno], &ied); /* this _MUST_ be the last ie added */
 		send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_POKE, 0, ied.buf, ied.pos, -1);
 	}
@@ -12497,7 +12465,7 @@ static struct ast_channel *iax2_request(const char *type, struct ast_format_cap
 	memset(&cai, 0, sizeof(cai));
 	cai.capability = iax2_capability;
 
-	ast_copy_flags64(&cai, &globalflags, IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE);
+	ast_copy_flags64(&cai, &globalflags, IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE);
 
 	/* Populate our address from the given */
 	if (create_addr(pds.peer, NULL, &addr, &cai)) {
@@ -12519,7 +12487,7 @@ static struct ast_channel *iax2_request(const char *type, struct ast_format_cap
 	}
 
 	/* If this is a trunk, update it now */
-	ast_copy_flags64(iaxs[callno], &cai, IAX_TRUNK | IAX_SENDANI | IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE);
+	ast_copy_flags64(iaxs[callno], &cai, IAX_TRUNK | IAX_SENDANI | IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE);
 	if (ast_test_flag64(&cai, IAX_TRUNK)) {
 		int new_callno;
 		if ((new_callno = make_trunk(callno, 1)) != -1)
@@ -12560,16 +12528,18 @@ static struct ast_channel *iax2_request(const char *type, struct ast_format_cap
 		if (!ast_format_cap_count(joint)) {
 			struct ast_format *best_fmt_cap = NULL;
 			struct ast_format *best_fmt_native = NULL;
+
 			res = ast_translator_best_choice(cap, ast_channel_nativeformats(c), &best_fmt_cap, &best_fmt_native);
 			if (res < 0) {
-				struct ast_str *native_cap_buf = ast_str_alloca(64);
-				struct ast_str *cap_buf = ast_str_alloca(64);
+				struct ast_str *native_cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+				struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 				ast_log(LOG_WARNING, "Unable to create translator path for %s to %s on %s\n",
 					ast_format_cap_get_names(ast_channel_nativeformats(c), &native_cap_buf),
 					ast_format_cap_get_names(cap, &cap_buf),
 					ast_channel_name(c));
 				ast_hangup(c);
+				ao2_ref(joint, -1);
 				return NULL;
 			}
 			ast_format_cap_append(joint, best_fmt_native, 0);
@@ -12837,7 +12807,7 @@ static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, st
 
 	if (peer) {
 		if (firstpass) {
-			ast_copy_flags64(peer, &globalflags, IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE | IAX_FORCE_ENCRYPT);
+			ast_copy_flags64(peer, &globalflags, IAX_USEJITTERBUF | IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE | IAX_FORCE_ENCRYPT);
 			peer->encmethods = iax2_encryption;
 			peer->adsi = adsi;
 			ast_string_field_set(peer, secret, "");
@@ -12924,8 +12894,6 @@ static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, st
 					ast_set_flags_to64(peer, IAX_NOTRANSFER|IAX_TRANSFERMEDIA, IAX_NOTRANSFER);
 			} else if (!strcasecmp(v->name, "jitterbuffer")) {
 				ast_set2_flag64(peer, ast_true(v->value), IAX_USEJITTERBUF);
-			} else if (!strcasecmp(v->name, "forcejitterbuffer")) {
-				ast_set2_flag64(peer, ast_true(v->value), IAX_FORCEJITTERBUF);
 			} else if (!strcasecmp(v->name, "host")) {
 				if (!strcasecmp(v->value, "dynamic")) {
 					/* They'll register with us */
@@ -13096,7 +13064,10 @@ static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, st
 
 		mailbox_specific_topic = ast_mwi_topic(peer->mailbox);
 		if (mailbox_specific_topic) {
-			peer->mwi_event_sub = stasis_subscribe_pool(mailbox_specific_topic, mwi_event_cb, NULL);
+			/* The MWI subscriptions exist just so the core knows we care about those
+			 * mailboxes.  However, we just grab the events out of the cache when it
+			 * is time to send MWI, since it is only sent with a REGACK. */
+			peer->mwi_event_sub = stasis_subscribe_pool(mailbox_specific_topic, stasis_subscription_cb_noop, NULL);
 		}
 	}
 
@@ -13171,7 +13142,7 @@ static struct iax2_user *build_user(const char *name, struct ast_variable *v, st
 			user->calltoken_required = CALLTOKEN_DEFAULT;
 			ast_string_field_set(user, name, name);
 			ast_string_field_set(user, language, language);
-			ast_copy_flags64(user, &globalflags, IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_CODEC_USER_FIRST | IAX_CODEC_NOPREFS | IAX_CODEC_NOCAP | IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE | IAX_FORCE_ENCRYPT);
+			ast_copy_flags64(user, &globalflags, IAX_USEJITTERBUF | IAX_CODEC_USER_FIRST | IAX_CODEC_NOPREFS | IAX_CODEC_NOCAP | IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE | IAX_FORCE_ENCRYPT);
 			ast_clear_flag64(user, IAX_HASCALLERID);
 			ast_string_field_set(user, cid_name, "");
 			ast_string_field_set(user, cid_num, "");
@@ -13253,8 +13224,6 @@ static struct iax2_user *build_user(const char *name, struct ast_variable *v, st
 				ast_set2_flag64(user, ast_true(v->value), IAX_IMMEDIATE);
 			} else if (!strcasecmp(v->name, "jitterbuffer")) {
 				ast_set2_flag64(user, ast_true(v->value), IAX_USEJITTERBUF);
-			} else if (!strcasecmp(v->name, "forcejitterbuffer")) {
-				ast_set2_flag64(user, ast_true(v->value), IAX_FORCEJITTERBUF);
 			} else if (!strcasecmp(v->name, "dbsecret")) {
 				ast_string_field_set(user, dbsecret, v->value);
 			} else if (!strcasecmp(v->name, "secret")) {
@@ -13467,7 +13436,7 @@ static void set_config_destroy(void)
 	amaflags = 0;
 	delayreject = 0;
 	ast_clear_flag64((&globalflags), IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF |
-		IAX_FORCEJITTERBUF | IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE);
+		IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE);
 	delete_users();
 	ao2_callback(callno_limits, OBJ_NODATA, addr_range_delme_cb, NULL);
 	ao2_callback(calltoken_ignores, OBJ_NODATA, addr_range_delme_cb, NULL);
@@ -13698,8 +13667,6 @@ static int set_config(const char *config_file, int reload, int forced)
 			}
 		} else if (!strcasecmp(v->name, "jitterbuffer"))
 			ast_set2_flag64((&globalflags), ast_true(v->value), IAX_USEJITTERBUF);
-		else if (!strcasecmp(v->name, "forcejitterbuffer"))
-			ast_set2_flag64((&globalflags), ast_true(v->value), IAX_FORCEJITTERBUF);
 		else if (!strcasecmp(v->name, "delayreject"))
 			delayreject = ast_true(v->value);
 		else if (!strcasecmp(v->name, "allowfwdownload"))
@@ -14418,7 +14385,7 @@ static int function_iaxpeer(struct ast_channel *chan, const char *cmd, char *dat
 	} else  if (!strcasecmp(colname, "callerid_num")) {
 		ast_copy_string(buf, peer->cid_num, len);
 	} else  if (!strcasecmp(colname, "codecs")) {
-		struct ast_str *codec_buf = ast_str_alloca(256);
+		struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 		iax2_getformatname_multiple(peer->capability, &codec_buf);
 		ast_copy_string(buf, ast_str_buffer(codec_buf), len);
@@ -14711,7 +14678,6 @@ static void cleanup_thread_list(void *head)
 
 static int __unload_module(void)
 {
-	struct ast_context *con;
 	int x;
 
 	network_change_stasis_unsubscribe();
@@ -14775,7 +14741,6 @@ static int __unload_module(void)
 	ao2_ref(users, -1);
 	ao2_ref(iax_peercallno_pvts, -1);
 	ao2_ref(iax_transfercallno_pvts, -1);
-	ao2_ref(peercnts, -1);
 	ao2_ref(callno_limits, -1);
 	ao2_ref(calltoken_ignores, -1);
 	if (timer) {
@@ -14783,12 +14748,13 @@ static int __unload_module(void)
 		timer = NULL;
 	}
 	transmit_processor = ast_taskprocessor_unreference(transmit_processor);
+
+	ast_sched_clean_by_callback(sched, peercnt_remove_cb, peercnt_remove_cb);
 	ast_sched_context_destroy(sched);
 	sched = NULL;
+	ao2_ref(peercnts, -1);
 
-	con = ast_context_find(regcontext);
-	if (con)
-		ast_context_destroy(con, "IAX2");
+	ast_context_destroy_by_name(regcontext, "IAX2");
 	ast_unload_realtime("iaxpeers");
 
 	ao2_ref(iax2_tech.capabilities, -1);
diff --git a/channels/chan_mgcp.c b/channels/chan_mgcp.c
index 546256e..16ba7ec 100644
--- a/channels/chan_mgcp.c
+++ b/channels/chan_mgcp.c
@@ -40,7 +40,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428687 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/socket.h>
 #include <sys/ioctl.h>
@@ -489,14 +489,6 @@ static struct ast_channel_tech mgcp_tech = {
 	.func_channel_read = acf_channel_read,
 };
 
-static void mwi_event_cb(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
-{
-	/* This module does not handle MWI in an event-based manner.  However, it
-	 * subscribes to MWI for each mailbox that is configured so that the core
-	 * knows that we care about it.  Then, chan_mgcp will get the MWI from the
-	 * event cache instead of checking the mailbox directly. */
-}
-
 static int has_voicemail(struct mgcp_endpoint *p)
 {
 	int new_msgs;
@@ -1261,7 +1253,7 @@ static int mgcp_write(struct ast_channel *ast, struct ast_frame *frame)
 		}
 	} else {
 		if (ast_format_cap_iscompatible_format(ast_channel_nativeformats(ast), frame->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
-			struct ast_str *cap_buf = ast_str_alloca(64);
+			struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 			ast_log(LOG_WARNING, "Asked to transmit frame type %s, while native formats is %s (read/write = %s/%s)\n",
 				ast_format_get_name(frame->subclass.format),
@@ -1994,9 +1986,9 @@ static int process_sdp(struct mgcp_subchannel *sub, struct mgcp_request *req)
 	int codec, codec_count=0;
 	int iterator;
 	struct mgcp_endpoint *p = sub->parent;
-	struct ast_str *global_buf = ast_str_alloca(64);
-	struct ast_str *peer_buf = ast_str_alloca(64);
-	struct ast_str *pvt_buf = ast_str_alloca(64);
+	struct ast_str *global_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+	struct ast_str *peer_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+	struct ast_str *pvt_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 	/* Get codec and RTP info from SDP */
 	m = get_sdp(req, "m");
@@ -3980,7 +3972,7 @@ static struct ast_channel *mgcp_request(const char *type, struct ast_format_cap
 	char tmp[256];
 
 	if (!(ast_format_cap_iscompatible(cap, global_capability))) {
-		struct ast_str *cap_buf = ast_str_alloca(64);
+		struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 		ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%s'\n",
 			ast_format_cap_get_names(cap, &cap_buf));
 		/*return NULL;*/
@@ -4237,7 +4229,11 @@ static struct mgcp_gateway *build_gateway(char *cat, struct ast_variable *v)
 
 					mailbox_specific_topic = ast_mwi_topic(e->mailbox);
 					if (mailbox_specific_topic) {
-						e->mwi_event_sub = stasis_subscribe_pool(mailbox_specific_topic, mwi_event_cb, NULL);
+						/* This module does not handle MWI in an event-based manner.  However, it
+						 * subscribes to MWI for each mailbox that is configured so that the core
+						 * knows that we care about it.  Then, chan_mgcp will get the MWI from the
+						 * event cache instead of checking the mailbox directly. */
+						e->mwi_event_sub = stasis_subscribe_pool(mailbox_specific_topic, stasis_subscription_cb_noop, NULL);
 					}
 				}
 				snprintf(e->rqnt_ident, sizeof(e->rqnt_ident), "%08lx", (unsigned long)ast_random());
@@ -4999,7 +4995,9 @@ static int unload_module(void)
 		return -1;
 	}
 
-	close(mgcpsock);
+	if (mgcpsock > -1) {
+		close(mgcpsock);
+	}
 	ast_rtp_glue_unregister(&mgcp_rtp_glue);
 	ast_cli_unregister_multiple(cli_mgcp, sizeof(cli_mgcp) / sizeof(struct ast_cli_entry));
 	ast_sched_context_destroy(sched);
diff --git a/channels/chan_misdn.c b/channels/chan_misdn.c
index 95ca173..ae8bcd7 100644
--- a/channels/chan_misdn.c
+++ b/channels/chan_misdn.c
@@ -81,7 +81,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 424472 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <pthread.h>
 #include <sys/socket.h>
@@ -7541,7 +7541,7 @@ static int misdn_write(struct ast_channel *ast, struct ast_frame *frame)
 		ast_debug(1, "write2mISDN %p %d bytes: ", p, frame->samples);
 
 		for (i = 0; i < max; i++) {
-			ast_debug(1, "%2.2x ", ((char *) frame->data.ptr)[i]);
+			ast_debug(1, "%02hhx ", ((unsigned char *) frame->data.ptr)[i]);
 		}
 	}
 #endif
@@ -9450,11 +9450,11 @@ static void misdn_facility_ie_handler(enum event_e event, struct misdn_bchannel
 		break;
 #endif	/* We don't handle this yet */
 	case Fac_SubaddressTransfer:
-		/* We do not have anything to do for this message since we do not handle subaddreses. */
+		/* We do not have anything to do for this message since we do not handle subaddresses. */
 		break;
 	case Fac_RequestSubaddress:
 		/*
-		 * We do not have anything to do for this message since we do not handle subaddreses.
+		 * We do not have anything to do for this message since we do not handle subaddresses.
 		 * However, we do care about some other ie's that should be present.
 		 */
 		if (bc->redirecting.to_changed) {
diff --git a/channels/chan_motif.c b/channels/chan_motif.c
index 11f0bb8..24575fa 100644
--- a/channels/chan_motif.c
+++ b/channels/chan_motif.c
@@ -44,7 +44,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 427982 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/socket.h>
 #include <fcntl.h>
@@ -1706,7 +1706,7 @@ static int jingle_write(struct ast_channel *ast, struct ast_frame *frame)
 	switch (frame->frametype) {
 	case AST_FRAME_VOICE:
 		if (ast_format_cap_iscompatible_format(ast_channel_nativeformats(ast), frame->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
-			struct ast_str *codec_buf = ast_str_alloca(64);
+			struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 			ast_log(LOG_WARNING,
 				"Asked to transmit frame type %s, while native formats is %s (read/write = %s/%s)\n",
diff --git a/channels/chan_multicast_rtp.c b/channels/chan_multicast_rtp.c
index de44bf1..267baab 100644
--- a/channels/chan_multicast_rtp.c
+++ b/channels/chan_multicast_rtp.c
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <fcntl.h>
 #include <sys/signal.h>
diff --git a/channels/chan_nbs.c b/channels/chan_nbs.c
index e49a8a9..2abde61 100644
--- a/channels/chan_nbs.c
+++ b/channels/chan_nbs.c
@@ -32,7 +32,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/socket.h>
 #include <sys/time.h>
@@ -232,7 +232,7 @@ static struct ast_channel *nbs_request(const char *type, struct ast_format_cap *
 	struct ast_channel *tmp = NULL;
 
 	if (ast_format_cap_iscompatible_format(cap, ast_format_slin) == AST_FORMAT_CMP_NOT_EQUAL) {
-		struct ast_str *cap_buf = ast_str_alloca(64);
+		struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 		ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%s'\n",
 			ast_format_cap_get_names(cap, &cap_buf));
diff --git a/channels/chan_oss.c b/channels/chan_oss.c
index 84a45ad..912a5eb 100644
--- a/channels/chan_oss.c
+++ b/channels/chan_oss.c
@@ -45,7 +45,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <ctype.h>		/* isalnum() used here */
 #include <math.h>
@@ -863,7 +863,7 @@ static struct ast_channel *oss_request(const char *type, struct ast_format_cap *
 		return NULL;
 	}
 	if (ast_format_cap_iscompatible_format(cap, ast_format_slin) == AST_FORMAT_CMP_NOT_EQUAL) {
-		struct ast_str *codec_buf = ast_str_alloca(64);
+		struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 		ast_log(LOG_NOTICE, "Format %s unsupported\n", ast_format_cap_get_names(cap, &codec_buf));
 		return NULL;
 	}
diff --git a/channels/chan_phone.c b/channels/chan_phone.c
index 390e250..451d9f2 100644
--- a/channels/chan_phone.c
+++ b/channels/chan_phone.c
@@ -40,7 +40,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 426570 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <ctype.h>
 #include <sys/socket.h>
@@ -1282,7 +1282,7 @@ static struct ast_channel *phone_request(const char *type, struct ast_format_cap
 	restart_monitor();
 	if (tmp == NULL) {
 		if (!(ast_format_cap_iscompatible(cap, phone_tech.capabilities))) {
-			struct ast_str *codec_buf = ast_str_alloca(64);
+			struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 			ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%s'\n",
 				ast_format_cap_get_names(cap, &codec_buf));
 			return NULL;
diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c
index 4c03e1a..0a8d1bc 100644
--- a/channels/chan_pjsip.c
+++ b/channels/chan_pjsip.c
@@ -38,7 +38,7 @@
 #include <pjsip_ua.h>
 #include <pjlib.h>
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428302 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/lock.h"
 #include "asterisk/channel.h"
@@ -73,7 +73,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428302 $")
 AST_THREADSTORAGE(uniqueid_threadbuf);
 #define UNIQUEID_BUFSIZE 256
 
-static const char desc[] = "PJSIP Channel";
 static const char channel_type[] = "PJSIP";
 
 static unsigned int chan_idx;
@@ -161,10 +160,17 @@ static struct ast_sip_session_supplement chan_pjsip_ack_supplement = {
 static enum ast_rtp_glue_result chan_pjsip_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance)
 {
 	struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(chan);
-	struct chan_pjsip_pvt *pvt = channel->pvt;
+	struct chan_pjsip_pvt *pvt;
 	struct ast_sip_endpoint *endpoint;
+	struct ast_datastore *datastore;
+
+	if (!channel || !channel->session || !(pvt = channel->pvt) || !pvt->media[SIP_MEDIA_AUDIO]->rtp) {
+		return AST_RTP_GLUE_RESULT_FORBID;
+	}
 
-	if (!pvt || !channel->session || !pvt->media[SIP_MEDIA_AUDIO]->rtp) {
+	datastore = ast_sip_session_get_datastore(channel->session, "t38");
+	if (datastore) {
+		ao2_ref(datastore, -1);
 		return AST_RTP_GLUE_RESULT_FORBID;
 	}
 
@@ -219,10 +225,13 @@ static void chan_pjsip_get_codec(struct ast_channel *chan, struct ast_format_cap
 
 static int send_direct_media_request(void *data)
 {
-	RAII_VAR(struct ast_sip_session *, session, data, ao2_cleanup);
+	struct ast_sip_session *session = data;
+	int res;
 
-	return ast_sip_session_refresh(session, NULL, NULL, NULL,
-			session->endpoint->media.direct_media.method, 1);
+	res = ast_sip_session_refresh(session, NULL, NULL, NULL,
+		session->endpoint->media.direct_media.method, 1);
+	ao2_ref(session, -1);
+	return res;
 }
 
 /*! \brief Destructor function for \ref transport_info_data */
@@ -386,7 +395,9 @@ static struct ast_channel *chan_pjsip_new(struct ast_sip_session *session, int s
 	chan = ast_channel_alloc_with_endpoint(1, state,
 		S_COR(session->id.number.valid, session->id.number.str, ""),
 		S_COR(session->id.name.valid, session->id.name.str, ""),
-		session->endpoint->accountcode, "", "", assignedids, requestor, 0,
+		session->endpoint->accountcode,
+		exten, session->endpoint->context,
+		assignedids, requestor, 0,
 		session->endpoint->persistent, "PJSIP/%s-%08x",
 		ast_sorcery_object_get_id(session->endpoint),
 		(unsigned) ast_atomic_fetchadd_int((int *) &chan_idx, +1));
@@ -418,12 +429,13 @@ static struct ast_channel *chan_pjsip_new(struct ast_sip_session *session, int s
 	ast_channel_nativeformats_set(chan, caps);
 
 	if (!ast_format_cap_empty(caps)) {
-		/*
-		 * XXX Probably should pick the first audio codec instead
-		 * of simply the first codec.  The first codec may be video.
-		 */
-		struct ast_format *fmt = ast_format_cap_get_format(caps, 0);
+		struct ast_format *fmt;
 
+		fmt = ast_format_cap_get_best_by_type(caps, AST_MEDIA_TYPE_AUDIO);
+		if (!fmt) {
+			/* Since our capabilities aren't empty, this will succeed */
+			fmt = ast_format_cap_get_format(caps, 0);
+		}
 		ast_channel_set_writeformat(chan, fmt);
 		ast_channel_set_rawwriteformat(chan, fmt);
 		ast_channel_set_readformat(chan, fmt);
@@ -442,8 +454,6 @@ static struct ast_channel *chan_pjsip_new(struct ast_sip_session *session, int s
 	ast_party_id_copy(&ast_channel_caller(chan)->id, &session->id);
 	ast_party_id_copy(&ast_channel_caller(chan)->ani, &session->id);
 
-	ast_channel_context_set(chan, session->endpoint->context);
-	ast_channel_exten_set(chan, S_OR(exten, "s"));
 	ast_channel_priority_set(chan, 1);
 
 	ast_channel_callgroup_set(chan, session->endpoint->pickup.callgroup);
@@ -490,7 +500,6 @@ static int answer(void *data)
 	struct ast_sip_session *session = data;
 
 	if (session->inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {
-		ao2_ref(session, -1);
 		return 0;
 	}
 
@@ -507,8 +516,6 @@ static int answer(void *data)
 		ast_sip_session_send_response(session, packet);
 	}
 
-	ao2_ref(session, -1);
-
 	return (status == PJ_SUCCESS) ? 0 : -1;
 }
 
@@ -516,19 +523,27 @@ static int answer(void *data)
 static int chan_pjsip_answer(struct ast_channel *ast)
 {
 	struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(ast);
+	struct ast_sip_session *session;
 
 	if (ast_channel_state(ast) == AST_STATE_UP) {
 		return 0;
 	}
 
 	ast_setstate(ast, AST_STATE_UP);
+	session = ao2_bump(channel->session);
 
-	ao2_ref(channel->session, +1);
-	if (ast_sip_push_task(channel->session->serializer, answer, channel->session)) {
+	/* the answer task needs to be pushed synchronously otherwise a race condition
+	   can occur between this thread and bridging (specifically when native bridging
+	   attempts to do direct media) */
+	ast_channel_unlock(ast);
+	if (ast_sip_push_task_synchronous(session->serializer, answer, session)) {
 		ast_log(LOG_WARNING, "Unable to push answer task to the threadpool. Cannot answer call\n");
-		ao2_cleanup(channel->session);
+		ao2_ref(session, -1);
+		ast_channel_lock(ast);
 		return -1;
 	}
+	ao2_ref(session, -1);
+	ast_channel_lock(ast);
 
 	return 0;
 }
@@ -540,7 +555,7 @@ static struct ast_frame *chan_pjsip_cng_tone_detected(struct ast_sip_session *se
 	int exists;
 
 	/* If we only needed this DSP for fax detection purposes we can just drop it now */
-	if (session->endpoint->dtmf == AST_SIP_DTMF_INBAND) {
+	if (session->endpoint->dtmf == AST_SIP_DTMF_INBAND || session->endpoint->dtmf == AST_SIP_DTMF_AUTO) {
 		ast_dsp_set_features(session->dsp, DSP_FEATURE_DIGIT_DETECT);
 	} else {
 		ast_dsp_free(session->dsp);
@@ -617,24 +632,19 @@ static struct ast_frame *chan_pjsip_read(struct ast_channel *ast)
 		return f;
 	}
 
+	ast_rtp_instance_set_last_rx(media->rtp, time(NULL));
+
 	if (f->frametype != AST_FRAME_VOICE) {
 		return f;
 	}
 
-	if (ast_format_cap_iscompatible_format(ast_channel_nativeformats(ast), f->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
-		struct ast_format_cap *caps;
+	if (ast_format_cap_iscompatible_format(channel->session->endpoint->media.codecs, f->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
+		ast_debug(1, "Oooh, got a frame with format of %s on channel '%s' when endpoint '%s' is not configured for it\n",
+			ast_format_get_name(f->subclass.format), ast_channel_name(ast),
+			ast_sorcery_object_get_id(channel->session->endpoint));
 
-		ast_debug(1, "Oooh, format changed to %s\n", ast_format_get_name(f->subclass.format));
-
-		caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
-		if (caps) {
-			ast_format_cap_append(caps, f->subclass.format, 0);
-			ast_channel_nativeformats_set(ast, caps);
-			ao2_ref(caps, -1);
-		}
-
-		ast_set_read_format(ast, ast_channel_readformat(ast));
-		ast_set_write_format(ast, ast_channel_writeformat(ast));
+		ast_frfree(f);
+		return &ast_null_frame;
 	}
 
 	if (channel->session->dsp) {
@@ -670,7 +680,7 @@ static int chan_pjsip_write(struct ast_channel *ast, struct ast_frame *frame)
 			return 0;
 		}
 		if (ast_format_cap_iscompatible_format(ast_channel_nativeformats(ast), frame->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
-			struct ast_str *cap_buf = ast_str_alloca(128);
+			struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 			struct ast_str *write_transpath = ast_str_alloca(256);
 			struct ast_str *read_transpath = ast_str_alloca(256);
 
@@ -911,7 +921,7 @@ static int chan_pjsip_queryoption(struct ast_channel *ast, int option, void *dat
 	struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(ast);
 	struct ast_sip_session *session = channel->session;
 	int res = -1;
-	enum ast_sip_session_t38state state = T38_STATE_UNAVAILABLE;
+	enum ast_t38_state state = T38_STATE_UNAVAILABLE;
 
 	switch (option) {
 	case AST_OPTION_T38_STATE:
@@ -1051,17 +1061,67 @@ static int transmit_info_with_vidupdate(void *data)
 	return 0;
 }
 
+/*!
+ * \internal
+ * \brief TRUE if a COLP update can be sent to the peer.
+ * \since 13.3.0
+ *
+ * \param session The session to see if the COLP update is allowed.
+ *
+ * \retval 0 Update is not allowed.
+ * \retval 1 Update is allowed.
+ */
+static int is_colp_update_allowed(struct ast_sip_session *session)
+{
+	struct ast_party_id connected_id;
+	int update_allowed = 0;
+
+	if (!session->endpoint->id.send_pai && !session->endpoint->id.send_rpid) {
+		return 0;
+	}
+
+	/*
+	 * Check if privacy allows the update.  Check while the channel
+	 * is locked so we can work with the shallow connected_id copy.
+	 */
+	ast_channel_lock(session->channel);
+	connected_id = ast_channel_connected_effective_id(session->channel);
+	if (connected_id.number.valid
+		&& (session->endpoint->id.trust_outbound
+			|| (ast_party_id_presentation(&connected_id) & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED)) {
+		update_allowed = 1;
+	}
+	ast_channel_unlock(session->channel);
+
+	return update_allowed;
+}
+
 /*! \brief Update connected line information */
 static int update_connected_line_information(void *data)
 {
-	RAII_VAR(struct ast_sip_session *, session, data, ao2_cleanup);
+	struct ast_sip_session *session = data;
 
-	if ((ast_channel_state(session->channel) != AST_STATE_UP) && (session->inv_session->role == PJSIP_UAS_ROLE)) {
-		int response_code = 0;
+	if (ast_channel_state(session->channel) == AST_STATE_UP
+		|| session->inv_session->role == PJSIP_ROLE_UAC) {
+		if (is_colp_update_allowed(session)) {
+			enum ast_sip_session_refresh_method method;
+			int generate_new_sdp;
 
-		if (session->inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {
-			return 0;
+			method = session->endpoint->id.refresh_method;
+			if (session->inv_session->invite_tsx
+				&& (session->inv_session->options & PJSIP_INV_SUPPORT_UPDATE)) {
+				method = AST_SIP_SESSION_REFRESH_METHOD_UPDATE;
+			}
+
+			/* Only the INVITE method actually needs SDP, UPDATE can do without */
+			generate_new_sdp = (method == AST_SIP_SESSION_REFRESH_METHOD_INVITE);
+
+			ast_sip_session_refresh(session, NULL, NULL, NULL, method, generate_new_sdp);
 		}
+	} else if (session->endpoint->rpid_immediate
+		&& session->inv_session->state != PJSIP_INV_STATE_DISCONNECTED
+		&& is_colp_update_allowed(session)) {
+		int response_code = 0;
 
 		if (ast_channel_state(session->channel) == AST_STATE_RING) {
 			response_code = !session->endpoint->inband_progress ? 180 : 183;
@@ -1076,34 +1136,9 @@ static int update_connected_line_information(void *data)
 				ast_sip_session_send_response(session, packet);
 			}
 		}
-	} else {
-		enum ast_sip_session_refresh_method method = session->endpoint->id.refresh_method;
-		int generate_new_sdp;
-		struct ast_party_id connected_id;
-
-		if (session->inv_session->invite_tsx && (session->inv_session->options & PJSIP_INV_SUPPORT_UPDATE)) {
-			method = AST_SIP_SESSION_REFRESH_METHOD_UPDATE;
-		}
-
-		/* Only the INVITE method actually needs SDP, UPDATE can do without */
-		generate_new_sdp = (method == AST_SIP_SESSION_REFRESH_METHOD_INVITE);
-
-		/*
-		 * We can get away with a shallow copy here because we are
-		 * not looking at strings.
-		 */
-		ast_channel_lock(session->channel);
-		connected_id = ast_channel_connected_effective_id(session->channel);
-		ast_channel_unlock(session->channel);
-
-		if ((session->endpoint->id.send_pai || session->endpoint->id.send_rpid) &&
-		    (session->endpoint->id.trust_outbound ||
-		     ((connected_id.name.presentation & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED &&
-		      (connected_id.number.presentation & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED))) {
-			ast_sip_session_refresh(session, NULL, NULL, NULL, method, generate_new_sdp);
-		}
 	}
 
+	ao2_ref(session, -1);
 	return 0;
 }
 
@@ -1324,6 +1359,8 @@ static void transfer_redirect(struct ast_sip_session *session, const char *targe
 	pj_str_t tmp;
 
 	if (pjsip_inv_end_session(session->inv_session, 302, NULL, &packet) != PJ_SUCCESS) {
+		ast_log(LOG_WARNING, "Failed to redirect PJSIP session for channel %s\n",
+			ast_channel_name(session->channel));
 		message = AST_TRANSFER_FAILED;
 		ast_queue_control_data(session->channel, AST_CONTROL_TRANSFER, &message, sizeof(message));
 
@@ -1336,6 +1373,8 @@ static void transfer_redirect(struct ast_sip_session *session, const char *targe
 
 	pj_strdup2_with_null(packet->pool, &tmp, target);
 	if (!(contact->uri = pjsip_parse_uri(packet->pool, tmp.ptr, tmp.slen, PJSIP_PARSE_URI_AS_NAMEADDR))) {
+		ast_log(LOG_WARNING, "Failed to parse destination URI '%s' for channel %s\n",
+			target, ast_channel_name(session->channel));
 		message = AST_TRANSFER_FAILED;
 		ast_queue_control_data(session->channel, AST_CONTROL_TRANSFER, &message, sizeof(message));
 		pjsip_tx_data_dec_ref(packet);
@@ -1354,6 +1393,8 @@ static void transfer_refer(struct ast_sip_session *session, const char *target)
 	enum ast_control_transfer message = AST_TRANSFER_SUCCESS;
 	pj_str_t tmp;
 	pjsip_tx_data *packet;
+	const char *ref_by_val;
+	char local_info[pj_strlen(&session->inv_session->dlg->local.info_str) + 1];
 
 	if (pjsip_xfer_create_uac(session->inv_session->dlg, NULL, &sub) != PJ_SUCCESS) {
 		message = AST_TRANSFER_FAILED;
@@ -1370,6 +1411,14 @@ static void transfer_refer(struct ast_sip_session *session, const char *target)
 		return;
 	}
 
+	ref_by_val = pbx_builtin_getvar_helper(session->channel, "SIPREFERREDBYHDR");
+	if (!ast_strlen_zero(ref_by_val)) {
+		ast_sip_add_header(packet, "Referred-By", ref_by_val);
+	} else {
+		ast_copy_pj_str(local_info, &session->inv_session->dlg->local.info_str, sizeof(local_info));
+		ast_sip_add_header(packet, "Referred-By", local_info);
+	}
+
 	pjsip_xfer_send_request(sub, packet);
 	ast_queue_control_data(session->channel, AST_CONTROL_TRANSFER, &message, sizeof(message));
 }
@@ -1377,14 +1426,28 @@ static void transfer_refer(struct ast_sip_session *session, const char *target)
 static int transfer(void *data)
 {
 	struct transfer_data *trnf_data = data;
+	struct ast_sip_endpoint *endpoint = NULL;
+	struct ast_sip_contact *contact = NULL;
+	const char *target = trnf_data->target;
+
+	/* See if we have an endpoint; if so, use its contact */
+	endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", target);
+	if (endpoint) {
+		contact = ast_sip_location_retrieve_contact_from_aor_list(endpoint->aors);
+		if (contact && !ast_strlen_zero(contact->uri)) {
+			target = contact->uri;
+		}
+	}
 
 	if (ast_channel_state(trnf_data->session->channel) == AST_STATE_RING) {
-		transfer_redirect(trnf_data->session, trnf_data->target);
+		transfer_redirect(trnf_data->session, target);
 	} else {
-		transfer_refer(trnf_data->session, trnf_data->target);
+		transfer_refer(trnf_data->session, target);
 	}
 
 	ao2_ref(trnf_data, -1);
+	ao2_cleanup(endpoint);
+	ao2_cleanup(contact);
 	return 0;
 }
 
@@ -1422,6 +1485,14 @@ static int chan_pjsip_digit_begin(struct ast_channel *chan, char digit)
 		}
 
 		ast_rtp_instance_dtmf_begin(media->rtp, digit);
+                break;
+	case AST_SIP_DTMF_AUTO:
+                       if (!media || !media->rtp || (ast_rtp_instance_dtmf_mode_get(media->rtp) == AST_RTP_DTMF_MODE_INBAND)) {
+                        return -1;
+                }
+
+                ast_rtp_instance_dtmf_begin(media->rtp, digit);
+                break;
 	case AST_SIP_DTMF_NONE:
 		break;
 	case AST_SIP_DTMF_INBAND:
@@ -1525,6 +1596,15 @@ static int chan_pjsip_digit_end(struct ast_channel *ast, char digit, unsigned in
 		}
 
 		ast_rtp_instance_dtmf_end_with_duration(media->rtp, digit, duration);
+                break;
+        case AST_SIP_DTMF_AUTO:
+                if (!media || !media->rtp || (ast_rtp_instance_dtmf_mode_get(media->rtp) == AST_RTP_DTMF_MODE_INBAND)) {
+                        return -1;
+                }
+
+                ast_rtp_instance_dtmf_end_with_duration(media->rtp, digit, duration);
+                break;
+
 	case AST_SIP_DTMF_NONE:
 		break;
 	case AST_SIP_DTMF_INBAND:
@@ -1687,22 +1767,7 @@ static int hangup(void *data)
 	struct ast_sip_session *session = channel->session;
 	int cause = h_data->cause;
 
-	if (!session->defer_terminate) {
-		pj_status_t status;
-		pjsip_tx_data *packet = NULL;
-
-		if (session->inv_session->state == PJSIP_INV_STATE_NULL) {
-			pjsip_inv_terminate(session->inv_session, cause ? cause : 603, PJ_TRUE);
-		} else if (((status = pjsip_inv_end_session(session->inv_session, cause ? cause : 603, NULL, &packet)) == PJ_SUCCESS)
-			&& packet) {
-			if (packet->msg->type == PJSIP_RESPONSE_MSG) {
-				ast_sip_session_send_response(session, packet);
-			} else {
-				ast_sip_session_send_request(session, packet);
-			}
-		}
-	}
-
+	ast_sip_session_terminate(session, cause);
 	clear_session_and_channel(session, ast, pvt);
 	ao2_cleanup(channel);
 	ao2_cleanup(h_data);
@@ -1714,9 +1779,17 @@ static int hangup(void *data)
 static int chan_pjsip_hangup(struct ast_channel *ast)
 {
 	struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(ast);
-	struct chan_pjsip_pvt *pvt = channel->pvt;
-	int cause = hangup_cause2sip(ast_channel_hangupcause(channel->session->channel));
-	struct hangup_data *h_data = hangup_data_alloc(cause, ast);
+	struct chan_pjsip_pvt *pvt;
+	int cause;
+	struct hangup_data *h_data;
+
+	if (!channel || !channel->session) {
+		return -1;
+	}
+
+	pvt = channel->pvt;
+	cause = hangup_cause2sip(ast_channel_hangupcause(channel->session->channel));
+	h_data = hangup_data_alloc(cause, ast);
 
 	if (!h_data) {
 		goto failure;
@@ -1778,6 +1851,7 @@ static int request(void *obj)
 	if (ast_strlen_zero(endpoint_name)) {
 		ast_log(LOG_ERROR, "Unable to create PJSIP channel with empty endpoint name\n");
 		req_data->cause = AST_CAUSE_CHANNEL_UNACCEPTABLE;
+		return -1;
 	} else if (!(endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", endpoint_name))) {
 		ast_log(LOG_ERROR, "Unable to create PJSIP channel - endpoint '%s' was not found\n", endpoint_name);
 		req_data->cause = AST_CAUSE_NO_ROUTE_DESTINATION;
@@ -1856,12 +1930,6 @@ static int sendtext(void *obj)
 		.body_text = data->text
 	};
 
-	/* NOT ast_strlen_zero, because a zero-length message is specifically
-	 * allowed by RFC 3428 (See section 10, Examples) */
-	if (!data->text) {
-		return 0;
-	}
-
 	ast_debug(3, "Sending in dialog SIP message\n");
 
 	ast_sip_create_request("MESSAGE", data->session->inv_session->dlg, data->session->endpoint, NULL, NULL, &tdata);
@@ -2024,6 +2092,22 @@ static int chan_pjsip_incoming_request(struct ast_sip_session *session, struct p
 		return 0;
 	}
 
+	/* Check for a to-tag to determine if this is a reinvite */
+	if (rdata->msg_info.to->tag.slen) {
+		/* Weird case. We've received a reinvite but we don't have a channel. The most
+		 * typical case for this happening is that a blind transfer fails, and so the
+		 * transferer attempts to reinvite himself back into the call. We already got
+		 * rid of that channel, and the other side of the call is unrecoverable.
+		 *
+		 * We treat this as a failure, so our best bet is to just hang this call
+		 * up and not create a new channel. Clearing defer_terminate here ensures that
+		 * calling ast_sip_session_terminate() can result in a BYE being sent ASAP.
+		 */
+		session->defer_terminate = 0;
+		ast_sip_session_terminate(session, 400);
+		return -1;
+	}
+
 	datastore = ast_sip_session_alloc_datastore(&transport_info, "transport_info");
 	if (!datastore) {
 		return -1;
@@ -2053,14 +2137,16 @@ static int chan_pjsip_incoming_request(struct ast_sip_session *session, struct p
 
 static int call_pickup_incoming_request(struct ast_sip_session *session, pjsip_rx_data *rdata)
 {
-	struct ast_features_pickup_config *pickup_cfg = ast_get_chan_features_pickup_config(session->channel);
+	struct ast_features_pickup_config *pickup_cfg;
 	struct ast_channel *chan;
 
-	/* We don't care about reinvites */
-	if (session->inv_session->state >= PJSIP_INV_STATE_CONFIRMED) {
+	/* Check for a to-tag to determine if this is a reinvite */
+	if (rdata->msg_info.to->tag.slen) {
+		/* We don't care about reinvites */
 		return 0;
 	}
 
+	pickup_cfg = ast_get_chan_features_pickup_config(session->channel);
 	if (!pickup_cfg) {
 		ast_log(LOG_ERROR, "Unable to retrieve pickup configuration options. Unable to detect call pickup extension.\n");
 		return 0;
@@ -2103,8 +2189,9 @@ static int pbx_start_incoming_request(struct ast_sip_session *session, pjsip_rx_
 {
 	int res;
 
-	/* We don't care about reinvites */
-	if (session->inv_session->state >= PJSIP_INV_STATE_CONFIRMED) {
+	/* Check for a to-tag to determine if this is a reinvite */
+	if (rdata->msg_info.to->tag.slen) {
+		/* We don't care about reinvites */
 		return 0;
 	}
 
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 2022402..06ea533 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -221,7 +221,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 429317 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <signal.h>
 #include <sys/signal.h>
@@ -254,7 +254,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 429317 $")
 #include "asterisk/astobj2.h"
 #include "asterisk/dnsmgr.h"
 #include "asterisk/devicestate.h"
-#include "asterisk/monitor.h"
 #include "asterisk/netsock2.h"
 #include "asterisk/localtime.h"
 #include "asterisk/abstract_jb.h"
@@ -998,9 +997,9 @@ static struct ao2_container *threadt;
 static struct ao2_container *peers;
 static struct ao2_container *peers_by_ip;
 
-/*! \brief  A bogus peer, to be used when authentication should fail */
-static struct sip_peer *bogus_peer;
-/*! \brief  We can recognise the bogus peer by this invalid MD5 hash */
+/*! \brief A bogus peer, to be used when authentication should fail */
+static AO2_GLOBAL_OBJ_STATIC(g_bogus_peer);
+/*! \brief We can recognize the bogus peer by this invalid MD5 hash */
 #define BOGUS_PEER_MD5SECRET "intentionally_invalid_md5_string"
 
 /*! \brief  The register list: Other SIP proxies we register with and receive calls from */
@@ -1180,7 +1179,11 @@ static int sip_send_mwi_to_peer(struct sip_peer *peer, int cache_only);
 static int __sip_autodestruct(const void *data);
 static int update_call_counter(struct sip_pvt *fup, int event);
 static int auto_congest(const void *arg);
-static struct sip_pvt *find_call(struct sip_request *req, struct ast_sockaddr *addr, const int intended_method);
+static struct sip_pvt *__find_call(struct sip_request *req, struct ast_sockaddr *addr, const int intended_method,
+	const char *file, int line, const char *func);
+#define find_call(req, addr, intended_method) \
+	__find_call(req, addr, intended_method, __FILE__, __LINE__, __PRETTY_FUNCTION__)
+
 static void build_route(struct sip_pvt *p, struct sip_request *req, int backwards, int resp);
 static int build_path(struct sip_pvt *p, struct sip_peer *peer, struct sip_request *req, const char *pathbuf);
 static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sockaddr *addr,
@@ -1446,7 +1449,7 @@ static int copy_via_headers(struct sip_pvt *p, struct sip_request *req, const st
 static void set_destination(struct sip_pvt *p, const char *uri);
 static void add_date(struct sip_request *req);
 static void add_expires(struct sip_request *req, int expires);
-static void build_contact(struct sip_pvt *p);
+static void build_contact(struct sip_pvt *p, struct sip_request *req, int incoming);
 
 /*------Request handling functions */
 static int handle_incoming(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, int *recount, int *nounlock);
@@ -1487,7 +1490,7 @@ static int  proc_session_timer(const void *vp);
 static void stop_session_timer(struct sip_pvt *p);
 static void start_session_timer(struct sip_pvt *p);
 static void restart_session_timer(struct sip_pvt *p);
-static const char *strefresherparam2str(enum st_refresher r);
+static const char *strefresherparam2str(enum st_refresher_param r);
 static int parse_session_expires(const char *p_hdrval, int *const p_interval, enum st_refresher_param *const p_ref);
 static int parse_minse(const char *p_hdrval, int *const p_interval);
 static int st_get_se(struct sip_pvt *, int max);
@@ -1705,6 +1708,7 @@ static int publish_expire(const void *data)
 	ast_assert(esc != NULL);
 
 	ao2_unlink(esc->compositor, esc_entry);
+	esc_entry->sched_id = -1;
 	ao2_ref(esc_entry, -1);
 	return 0;
 }
@@ -1737,6 +1741,11 @@ static struct sip_esc_entry *create_esc_entry(struct event_state_compositor *esc
 	/* Bump refcount for scheduler */
 	ao2_ref(esc_entry, +1);
 	esc_entry->sched_id = ast_sched_add(sched, expires_ms, publish_expire, esc_entry);
+	if (esc_entry->sched_id == -1) {
+		ao2_ref(esc_entry, -1);
+		ao2_ref(esc_entry, -1);
+		return NULL;
+	}
 
 	/* Note: This links the esc_entry into the ESC properly */
 	create_new_sip_etag(esc_entry, 0);
@@ -2331,30 +2340,6 @@ static struct ast_tcptls_session_args sip_tls_desc = {
 	\return Always returns 0 */
 #define append_history(p, event, fmt , args... )	append_history_full(p, "%-15s " fmt, event, ## args)
 
-struct sip_pvt *dialog_ref_debug(struct sip_pvt *p, const char *tag, char *file, int line, const char *func)
-{
-	if (p)
-#ifdef REF_DEBUG
-		__ao2_ref_debug(p, 1, tag, file, line, func);
-#else
-		ao2_ref(p, 1);
-#endif
-	else
-		ast_log(LOG_ERROR, "Attempt to Ref a null pointer\n");
-	return p;
-}
-
-struct sip_pvt *dialog_unref_debug(struct sip_pvt *p, const char *tag, char *file, int line, const char *func)
-{
-	if (p)
-#ifdef REF_DEBUG
-		__ao2_ref_debug(p, -1, tag, file, line, func);
-#else
-		ao2_ref(p, -1);
-#endif
-	return NULL;
-}
-
 /*! \brief map from an integer value to a string.
  * If no match is found, return errorstring
  */
@@ -3154,41 +3139,6 @@ cleanup:
 	return NULL;
 }
 
-#ifdef REF_DEBUG
-struct sip_peer *_ref_peer(struct sip_peer *peer, char *tag, char *file, int line, const char *func)
-{
-	if (peer)
-		__ao2_ref_debug(peer, 1, tag, file, line, func);
-	else
-		ast_log(LOG_ERROR, "Attempt to Ref a null peer pointer\n");
-	return peer;
-}
-
-void *_unref_peer(struct sip_peer *peer, char *tag, char *file, int line, const char *func)
-{
-	if (peer)
-		__ao2_ref_debug(peer, -1, tag, file, line, func);
-	return NULL;
-}
-#else
-/*!
- * helper functions to unreference various types of objects.
- * By handling them this way, we don't have to declare the
- * destructor on each call, which removes the chance of errors.
- */
-void *sip_unref_peer(struct sip_peer *peer, char *tag)
-{
-	ao2_t_ref(peer, -1, tag);
-	return NULL;
-}
-
-struct sip_peer *sip_ref_peer(struct sip_peer *peer, char *tag)
-{
-	ao2_t_ref(peer, 1, tag);
-	return peer;
-}
-#endif /* REF_DEBUG */
-
 static void peer_sched_cleanup(struct sip_peer *peer)
 {
 	if (peer->pokeexpire != -1) {
@@ -3231,10 +3181,24 @@ static int match_and_cleanup_peer_sched(void *peerobj, void *arg, int flags)
 
 static void unlink_peers_from_tables(peer_unlink_flag_t flag)
 {
-	ao2_t_callback(peers, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE,
+	struct ao2_iterator *peers_iter;
+
+	/*
+	 * We must remove the ref outside of the peers container to prevent
+	 * a deadlock condition when unsubscribing from stasis while it is
+	 * invoking a subscription event callback.
+	 */
+	peers_iter = ao2_t_callback(peers, OBJ_UNLINK | OBJ_MULTIPLE,
 		match_and_cleanup_peer_sched, &flag, "initiating callback to remove marked peers");
-	ao2_t_callback(peers_by_ip, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE,
+	if (peers_iter) {
+		ao2_iterator_destroy(peers_iter);
+	}
+
+	peers_iter = ao2_t_callback(peers_by_ip, OBJ_UNLINK | OBJ_MULTIPLE,
 		match_and_cleanup_peer_sched, &flag, "initiating callback to remove marked peers_by_ip");
+	if (peers_iter) {
+		ao2_iterator_destroy(peers_iter);
+	}
 }
 
 /* \brief Unlink all marked peers from ao2 containers */
@@ -3336,6 +3300,10 @@ void dialog_unlink_all(struct sip_pvt *dialog)
 
 	AST_SCHED_DEL_UNREF(sched, dialog->initid, dialog_unref(dialog, "when you delete the initid sched, you should dec the refcount for the stored dialog ptr"));
 
+	if (dialog->reinviteid > -1) {
+		AST_SCHED_DEL_UNREF(sched, dialog->reinviteid, dialog_unref(dialog, "clear ref for reinvite_timeout"));
+	}
+
 	if (dialog->autokillid > -1) {
 		AST_SCHED_DEL_UNREF(sched, dialog->autokillid, dialog_unref(dialog, "when you delete the autokillid sched, you should dec the refcount for the stored dialog ptr"));
 	}
@@ -3755,7 +3723,7 @@ static int __sip_xmit(struct sip_pvt *p, struct ast_str *data)
 	} else if (p->socket.tcptls_session) {
 		res = sip_tcptls_write(p->socket.tcptls_session, ast_str_buffer(data), ast_str_strlen(data));
 	} else if (p->socket.ws_session) {
-		if (!(res = ast_websocket_write(p->socket.ws_session, AST_WEBSOCKET_OPCODE_TEXT, ast_str_buffer(data), ast_str_strlen(data)))) {
+		if (!(res = ast_websocket_write_string(p->socket.ws_session, ast_str_buffer(data)))) {
 			/* The WebSocket API just returns 0 on success and -1 on failure, while this code expects the payload length to be returned */
 			res = ast_str_strlen(data);
 		}
@@ -3824,7 +3792,7 @@ static void ast_sip_ouraddrfor(const struct ast_sockaddr *them, struct ast_socka
 	ast_ouraddrfor(them, us);
 	ast_sockaddr_copy(&theirs, them);
 
-	if (ast_sockaddr_is_ipv6(&theirs)) {
+	if (ast_sockaddr_is_ipv6(&theirs) && !ast_sockaddr_is_ipv4_mapped(&theirs)) {
 		if (localaddr && !ast_sockaddr_isnull(&externaddr) && !ast_sockaddr_is_any(&bindaddr)) {
 			ast_log(LOG_WARNING, "Address remapping activated in sip.conf "
 				"but we're using IPv6, which doesn't need it. Please "
@@ -3997,6 +3965,13 @@ static int retrans_pkt(const void *data)
 			}
 
 			/* For non-invites, a maximum of 4 secs */
+			if (INT_MAX / pkt->timer_a < pkt->timer_t1) {
+				/*
+				 * Uh Oh, we will have an integer overflow.
+				 * Recalculate previous timeout time instead.
+				 */
+				pkt->timer_a = pkt->timer_a / 2;
+			}
 			siptimer_a = pkt->timer_t1 * pkt->timer_a;	/* Double each time */
 			if (pkt->method != SIP_INVITE && siptimer_a > 4000) {
 				siptimer_a = 4000;
@@ -4356,7 +4331,7 @@ void sip_scheddestroy(struct sip_pvt *p, int ms)
 	}
 	p->autokillid = ast_sched_add(sched, ms, __sip_autodestruct, dialog_ref(p, "setting ref as passing into ast_sched_add for __sip_autodestruct"));
 
-	if (p->stimer && p->stimer->st_active == TRUE && p->stimer->st_schedid > 0) {
+	if (p->stimer && p->stimer->st_active == TRUE && p->stimer->st_schedid > -1) {
 		stop_session_timer(p);
 	}
 }
@@ -4784,6 +4759,11 @@ static int sip_queryoption(struct ast_channel *chan, int option, void *data, int
 	struct sip_pvt *p = (struct sip_pvt *) ast_channel_tech_pvt(chan);
 	char *cp;
 
+	if (!p) {
+		ast_debug(1, "Attempt to Ref a null pointer. Sip private structure is gone!\n");
+		return -1;
+	}
+
 	sip_pvt_lock(p);
 
 	switch (option) {
@@ -5056,7 +5036,7 @@ static void register_peer_exten(struct sip_peer *peer, int onoff)
 static void destroy_mailbox(struct sip_mailbox *mailbox)
 {
 	if (mailbox->event_sub) {
-		mailbox->event_sub = stasis_unsubscribe(mailbox->event_sub);
+		mailbox->event_sub = stasis_unsubscribe_and_join(mailbox->event_sub);
 	}
 	ast_free(mailbox);
 }
@@ -7247,7 +7227,7 @@ static int sip_write(struct ast_channel *ast, struct ast_frame *frame)
 	switch (frame->frametype) {
 	case AST_FRAME_VOICE:
 		if (ast_format_cap_iscompatible_format(ast_channel_nativeformats(ast), frame->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
-			struct ast_str *codec_buf = ast_str_alloca(64);
+			struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 			ast_log(LOG_WARNING, "Asked to transmit frame type %s, while native formats is %s read/write = %s/%s\n",
 				ast_format_get_name(frame->subclass.format),
 				ast_format_cap_get_names(ast_channel_nativeformats(ast), &codec_buf),
@@ -7388,7 +7368,7 @@ static int sip_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
 		   redirect of both channels). Note that a channel can not be masqueraded *into*
 		   a native bridge. So there is no danger that this breaks a native bridge that
 		   should stay up. */
-		sip_set_rtp_peer(newchan, NULL, NULL, 0, 0, 0);
+		sip_set_rtp_peer(newchan, NULL, NULL, NULL, NULL, 0);
 		ret = 0;
 	}
 	ast_debug(3, "SIP Fixup: New owner for dialogue %s: %s (Old parent: %s)\n", p->callid, ast_channel_name(p->owner), ast_channel_name(oldchan));
@@ -7884,7 +7864,7 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *tit
 	struct ast_variable *v = NULL;
 	struct ast_format *fmt;
 	struct ast_format_cap *what = NULL; /* SHALLOW COPY DO NOT DESTROY! */
-	struct ast_str *codec_buf = ast_str_alloca(64);
+	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 	int needvideo = 0;
 	int needtext = 0;
 	char *exten;
@@ -8369,7 +8349,7 @@ static struct ast_frame *sip_rtp_read(struct ast_channel *ast, struct sip_pvt *p
 				}
 				ast_str_append(&out, 0, " -> ");
 				for (i = 0; i < f->datalen; i++) {
-					ast_str_append(&out, 0, "%02X ", (unsigned)arr[i]);
+					ast_str_append(&out, 0, "%02hhX ", arr[i]);
 				}
 				ast_verb(0, "%s\n", ast_str_buffer(out));
 				ast_free(out);
@@ -8644,13 +8624,18 @@ static void sip_pvt_callid_set(struct sip_pvt *pvt, struct ast_callid *callid)
  * Returns a reference to the object so whoever uses it later must
  * remember to release the reference.
  */
-struct sip_pvt *sip_alloc(ast_string_field callid, struct ast_sockaddr *addr,
-				 int useglobal_nat, const int intended_method, struct sip_request *req, struct ast_callid *logger_callid)
+struct sip_pvt *__sip_alloc(ast_string_field callid, struct ast_sockaddr *addr,
+				 int useglobal_nat, const int intended_method, struct sip_request *req, struct ast_callid *logger_callid,
+				 const char *file, int line, const char *func)
 {
 	struct sip_pvt *p;
 
-	if (!(p = ao2_t_alloc(sizeof(*p), sip_destroy_fn, "allocate a dialog(pvt) struct")))
+	p = __ao2_alloc_debug(sizeof(*p), sip_destroy_fn,
+		AO2_ALLOC_OPT_LOCK_MUTEX, "allocate a dialog(pvt) struct",
+		file, line, func, 1);
+	if (!p) {
 		return NULL;
+	}
 
 	if (ast_string_field_init(p, 512)) {
 		ao2_t_ref(p, -1, "failed to string_field_init, drop p");
@@ -8808,7 +8793,7 @@ struct sip_pvt *sip_alloc(ast_string_field callid, struct ast_sockaddr *addr,
  * This function will update the destination of the response according to the
  * Via header in the request and RFC 3261 section 18.2.2. We do not have a
  * transport layer so we ignore certain values like the 'received' param (we
- * set the destination address to the addres the request came from in the
+ * set the destination address to the address the request came from in the
  * respprep() function).
  *
  * \retval -1 error
@@ -9172,7 +9157,8 @@ static void sip_set_owner(struct sip_pvt *p, struct ast_channel *chan)
  * Returns a reference to the sip_pvt object, remember to give it back once done.
  *     Called by handle_request_do
  */
-static struct sip_pvt *find_call(struct sip_request *req, struct ast_sockaddr *addr, const int intended_method)
+static struct sip_pvt *__find_call(struct sip_request *req, struct ast_sockaddr *addr, const int intended_method,
+	const char *file, int line, const char *func)
 {
 	char totag[128];
 	char fromtag[128];
@@ -9224,11 +9210,13 @@ static struct sip_pvt *find_call(struct sip_request *req, struct ast_sockaddr *a
 		}
 	}
 
-	if (!sip_cfg.pedanticsipchecking) {
+	/* match on callid only for REGISTERs */
+	if (!sip_cfg.pedanticsipchecking || req->method == SIP_REGISTER) {
 		struct sip_pvt tmp_dialog = {
 			.callid = callid,
 		};
-		sip_pvt_ptr = ao2_t_find(dialogs, &tmp_dialog, OBJ_POINTER, "ao2_find in dialogs");
+		sip_pvt_ptr = __ao2_find_debug(dialogs, &tmp_dialog, OBJ_POINTER,
+			"find_call in dialogs", file, line, func);
 		if (sip_pvt_ptr) {  /* well, if we don't find it-- what IS in there? */
 			/* Found the call */
 			return sip_pvt_ptr;
@@ -9242,11 +9230,12 @@ static struct sip_pvt *find_call(struct sip_request *req, struct ast_sockaddr *a
 		struct sip_pvt *fork_pvt = NULL;
 		struct match_req_args args = { 0, };
 		int found;
-		struct ao2_iterator *iterator = ao2_t_callback(dialogs,
+		struct ao2_iterator *iterator = __ao2_callback_debug(dialogs,
 			OBJ_POINTER | OBJ_MULTIPLE,
 			dialog_find_multiple,
 			&tmp_dialog,
-			"pedantic ao2_find in dialogs");
+			"pedantic ao2_find in dialogs",
+			file, line, func);
 		struct sip_via *via = NULL;
 
 		args.method = req->method;
@@ -9283,6 +9272,15 @@ static struct sip_pvt *find_call(struct sip_request *req, struct ast_sockaddr *a
 
 			switch (found) {
 			case SIP_REQ_MATCH:
+				sip_pvt_lock(sip_pvt_ptr);
+				if (args.method != SIP_RESPONSE && args.authentication_present
+						&& strcmp(args.fromtag, sip_pvt_ptr->theirtag)) {
+					/* If we have a request that uses athentication and the fromtag is
+					 * different from that in the original call dialog, update the
+					 * fromtag in the saved call dialog */
+					ast_string_field_set(sip_pvt_ptr, theirtag, args.fromtag);
+				}
+				sip_pvt_unlock(sip_pvt_ptr);
 				ao2_iterator_destroy(iterator);
 				dialog_unref(fork_pvt, "unref fork_pvt");
 				free_via(via);
@@ -9291,7 +9289,8 @@ static struct sip_pvt *find_call(struct sip_request *req, struct ast_sockaddr *a
 				/* This is likely a forked Request that somehow resulted in us receiving multiple parts of the fork.
 			 	* RFC 3261 section 8.2.2.2, Indicate that we want to merge requests by sending a 482 response. */
 				transmit_response_using_temp(callid, addr, 1, intended_method, req, "482 (Loop Detected)");
-				dialog_unref(sip_pvt_ptr, "pvt did not match incoming SIP msg, unref from search.");
+				__ao2_ref_debug(sip_pvt_ptr, -1, "pvt did not match incoming SIP msg, unref from search.",
+					file, line, func);
 				ao2_iterator_destroy(iterator);
 				dialog_unref(fork_pvt, "unref fork_pvt");
 				free_via(via);
@@ -9302,7 +9301,8 @@ static struct sip_pvt *find_call(struct sip_request *req, struct ast_sockaddr *a
 				/* fall through */
 			case SIP_REQ_NOT_MATCH:
 			default:
-				dialog_unref(sip_pvt_ptr, "pvt did not match incoming SIP msg, unref from search");
+				__ao2_ref_debug(sip_pvt_ptr, -1, "pvt did not match incoming SIP msg, unref from search",
+					file, line, func);
 				break;
 			}
 		}
@@ -9386,6 +9386,9 @@ static int sip_register(const char *value, int lineno)
 		return -1;
 	}
 
+	reg->expire = -1;
+	reg->timeout = -1;
+
 	if (ast_string_field_init(reg, 256)) {
 		ao2_t_ref(reg, -1, "failed to string_field_init, drop reg");
 		return -1;
@@ -9459,6 +9462,8 @@ static int sip_subscribe_mwi(const char *value, int lineno)
 		return -1;
 	}
 
+	mwi->resub = -1;
+
 	if (ast_string_field_init(mwi, 256)) {
 		ao2_t_ref(mwi, -1, "failed to string_field_init, drop mwi");
 		return -1;
@@ -9473,7 +9478,6 @@ static int sip_subscribe_mwi(const char *value, int lineno)
 	}
 	ast_string_field_set(mwi, hostname, hostname);
 	ast_string_field_set(mwi, mailbox, mailbox);
-	mwi->resub = -1;
 	mwi->portno = portnum;
 	mwi->transport = transport;
 
@@ -9635,7 +9639,8 @@ static int parse_request(struct sip_request *req)
 {
 	char *c = ast_str_buffer(req->data);
 	ptrdiff_t *dst = req->header;
-	int i = 0, lim = SIP_MAX_HEADERS - 1;
+	int i = 0;
+	unsigned int lim = SIP_MAX_HEADERS - 1;
 	unsigned int skipping_headers = 0;
 	ptrdiff_t current_header_offset = 0;
 	char *previous_header = "";
@@ -9938,7 +9943,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
 	int debug = sip_debug_test_pvt(p);
 
 	/* START UNKNOWN */
-	struct ast_str *codec_buf = ast_str_alloca(64);
+	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 	struct ast_format *tmp_fmt;
 	/* END UNKNOWN */
 
@@ -10526,6 +10531,12 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
 		goto process_sdp_cleanup;
 	}
 
+	if (p->srtp && p->udptl && udptlportno != -1) {
+		ast_debug(1, "Terminating SRTP due to T.38 UDPTL\n");
+		ast_sdp_srtp_destroy(p->srtp);
+		p->srtp = NULL;
+	}
+
 	if (secure_audio && !(p->srtp && (ast_test_flag(p->srtp, AST_SRTP_CRYPTO_OFFER_OK)))) {
 		ast_log(LOG_WARNING, "Can't provide secure audio requested in SDP offer\n");
 		res = -1;
@@ -10533,7 +10544,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
 	}
 
 	if (!secure_audio && p->srtp) {
-		ast_log(LOG_WARNING, "We are requesting SRTP for audio, but they responded without it!\n");
+		ast_log(LOG_WARNING, "Failed to receive SDP offer/answer with required SRTP crypto attributes for audio\n");
 		res = -1;
 		goto process_sdp_cleanup;
 	}
@@ -10545,12 +10556,12 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
 	}
 
 	if (!p->novideo && !secure_video && p->vsrtp) {
-		ast_log(LOG_WARNING, "We are requesting SRTP for video, but they responded without it!\n");
+		ast_log(LOG_WARNING, "Failed to receive SDP offer/answer with required SRTP crypto attributes for video\n");
 		res = -1;
 		goto process_sdp_cleanup;
 	}
 
-	if (!(secure_audio || secure_video) && ast_test_flag(&p->flags[1], SIP_PAGE2_USE_SRTP)) {
+	if (!(secure_audio || secure_video || (p->udptl && udptlportno != -1)) && ast_test_flag(&p->flags[1], SIP_PAGE2_USE_SRTP)) {
 		ast_log(LOG_WARNING, "Matched device setup to use SRTP, but request was not!\n");
 		res = -1;
 		goto process_sdp_cleanup;
@@ -10581,11 +10592,11 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
 
 	if (debug) {
 		/* shame on whoever coded this.... */
-		struct ast_str *cap_buf = ast_str_alloca(64);
-		struct ast_str *peer_buf = ast_str_alloca(64);
-		struct ast_str *vpeer_buf = ast_str_alloca(64);
-		struct ast_str *tpeer_buf = ast_str_alloca(64);
-		struct ast_str *joint_buf = ast_str_alloca(64);
+		struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+		struct ast_str *peer_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+		struct ast_str *vpeer_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+		struct ast_str *tpeer_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+		struct ast_str *joint_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 		ast_verbose("Capabilities: us - %s, peer - audio=%s/video=%s/text=%s, combined - %s\n",
 			    ast_format_cap_get_names(p->caps, &cap_buf),
@@ -10629,7 +10640,11 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
 	/* Setup audio address and port */
 	if (p->rtp) {
 		if (sa && portno > 0) {
-			start_ice(p->rtp, (req->method != SIP_RESPONSE) ? 0 : 1);
+			/* Start ICE negotiation here, only when it is response, and setting that we are conrolling agent,
+			   as we are offerer */
+			if (req->method == SIP_RESPONSE) {
+				start_ice(p->rtp, 1);
+			}
 			ast_sockaddr_set_port(sa, portno);
 			ast_rtp_instance_set_remote_address(p->rtp, sa);
 			if (debug) {
@@ -10792,8 +10807,8 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
 		unsigned int framing;
 
 		if (debug) {
-			struct ast_str *cap_buf = ast_str_alloca(64);
-			struct ast_str *joint_buf = ast_str_alloca(64);
+			struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+			struct ast_str *joint_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 			ast_debug(1, "Setting native formats after processing SDP. peer joint formats %s, old nativeformats %s\n",
 				ast_format_cap_get_names(p->jointcaps, &joint_buf),
@@ -11106,7 +11121,7 @@ static int process_sdp_a_audio(const char *a, struct sip_pvt *p, struct ast_rtp_
 
 		if (framing && p->autoframing) {
 			ast_debug(1, "Setting framing to %ld\n", framing);
-			ast_rtp_codecs_set_framing(ast_rtp_instance_get_codecs(p->rtp), framing);
+			ast_format_cap_set_framing(p->caps, framing);
 		}
 		found = TRUE;
 	} else if (sscanf(a, "rtpmap: %30u %127[^/]/%30u", &codec, mimeSubtype, &sample_rate) == 3) {
@@ -11970,13 +11985,15 @@ static int reqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod, ui
 
 	add_header(req, "Via", p->via);
 	/*
-	 * Use the learned route set unless this is a CANCEL on an ACK for a non-2xx
+	 * Use the learned route set unless this is a CANCEL or an ACK for a non-2xx
 	 * final response. For a CANCEL or ACK, we have to send to the same destination
 	 * as the original INVITE.
+	 * Send UPDATE to the same destination as CANCEL, if call is not in final state.
 	 */
 	if (!sip_route_empty(&p->route) &&
 			!(sipmethod == SIP_CANCEL ||
-				(sipmethod == SIP_ACK && (p->invitestate == INV_COMPLETED || p->invitestate == INV_CANCELLED)))) {
+				(sipmethod == SIP_ACK && (p->invitestate == INV_COMPLETED || p->invitestate == INV_CANCELLED)) ||
+				(sipmethod == SIP_UPDATE && (p->invitestate == INV_PROCEEDING || p->invitestate == INV_EARLY_MEDIA)))) {
 		if (p->socket.type != AST_TRANSPORT_UDP && p->socket.tcptls_session) {
 			/* For TCP/TLS sockets that are connected we won't need
 			 * to do any hostname/IP lookups */
@@ -12820,9 +12837,6 @@ static void add_codec_to_sdp(const struct sip_pvt *p,
 	} else if (ast_format_cmp(format, ast_format_g723) == AST_FORMAT_CMP_EQUAL) {
 		/* Indicate that we don't support VAD (G.723.1 annex A) */
 		ast_str_append(a_buf, 0, "a=fmtp:%d annexa=no\r\n", rtp_code);
-	} else if (ast_format_cmp(format, ast_format_ilbc) == AST_FORMAT_CMP_EQUAL) {
-		/* Add information about us using only 20/30 ms packetization */
-		ast_str_append(a_buf, 0, "a=fmtp:%d mode=%u\r\n", rtp_code, framing);
 	} else if (ast_format_cmp(format, ast_format_siren7) == AST_FORMAT_CMP_EQUAL) {
 		/* Indicate that we only expect 32Kbps */
 		ast_str_append(a_buf, 0, "a=fmtp:%d bitrate=32000\r\n", rtp_code);
@@ -13128,7 +13142,7 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
 	int min_video_packet_size = 0;
 	int min_text_packet_size = 0;
 
-	struct ast_str *codec_buf = ast_str_alloca(64);
+	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 	/* Set the SDP session name */
 	snprintf(subject, sizeof(subject), "s=%s\r\n", ast_strlen_zero(global_sdpsession) ? "-" : global_sdpsession);
@@ -13344,7 +13358,7 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
 		}
 
 		/* Finally our remaining audio/video codecs */
-		for (x = 0; x < ast_format_cap_count(p->caps); x++) {
+		for (x = 0; ast_test_flag(&p->flags[0], SIP_OUTGOING) && x < ast_format_cap_count(p->caps); x++) {
 			tmp_fmt = ast_format_cap_get_format(p->caps, x);
 
 			if (ast_format_cap_iscompatible_format(alreadysent, tmp_fmt) != AST_FORMAT_CMP_NOT_EQUAL) {
@@ -13396,9 +13410,19 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
 			ast_str_append(&a_audio, 0, "a=maxptime:%d\r\n", max_audio_packet_size);
 		}
 
+		if (!ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
+			ast_debug(1, "Setting framing on incoming call: %u\n", min_audio_packet_size);
+			ast_rtp_codecs_set_framing(ast_rtp_instance_get_codecs(p->rtp), min_audio_packet_size);
+		}
+
 		if (!doing_directmedia) {
 			if (ast_test_flag(&p->flags[2], SIP_PAGE3_ICE_SUPPORT)) {
 				add_ice_to_sdp(p->rtp, &a_audio);
+				/* Start ICE negotiation, and setting that we are controlled agent,
+				   as this is response to offer */
+				if (resp->method == SIP_RESPONSE) {
+					start_ice(p->rtp, 0);
+				}
 			}
 
 			add_dtls_to_sdp(p->rtp, &a_audio);
@@ -13688,10 +13712,6 @@ static int transmit_response_with_sdp(struct sip_pvt *p, const char *msg, const
 		add_cc_call_info_to_response(p, &resp);
 	}
 	if (p->rtp) {
-		if (!p->autoframing && !ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
-			ast_debug(1, "Setting framing from config on incoming call\n");
-			ast_rtp_codecs_set_framing(ast_rtp_instance_get_codecs(p->rtp), ast_format_cap_get_framing(p->caps));
-		}
 		ast_rtp_instance_activate(p->rtp);
 		try_suggested_sip_codec(p);
 		if (p->t38.state == T38_ENABLED) {
@@ -13829,22 +13849,110 @@ static void extract_uri(struct sip_pvt *p, struct sip_request *req)
 	if (!ast_strlen_zero(c)) {
 		ast_string_field_set(p, uri, c);
 	}
+}
+
+/*!
+ * \brief Determine if, as a UAS, we need to use a SIPS Contact.
+ *
+ * This uses the rules defined in RFC 3261 section 12.1.1 to
+ * determine if a SIPS URI should be used as the Contact header
+ * when responding to incoming SIP requests.
+ *
+ * \param req The incoming SIP request
+ * \retval 0 SIPS is not required
+ * \retval 1 SIPS is required
+ */
+static int uas_sips_contact(struct sip_request *req)
+{
+	const char *record_route = sip_get_header(req, "Record-Route");
 
+	if (!strncmp(REQ_OFFSET_TO_STR(req, rlpart2), "sips:", 5)) {
+		return 1;
+	}
+
+	if (record_route) {
+		char *record_route_uri = get_in_brackets(ast_strdupa(record_route));
+
+		if (!strncmp(record_route_uri, "sips:", 5)) {
+			return 1;
+		}
+	} else {
+		const char *contact = sip_get_header(req, "Contact");
+		char *contact_uri = get_in_brackets(ast_strdupa(contact));
+
+		if (!strncmp(contact_uri, "sips:", 5)) {
+			return 1;
+		}
+	}
+
+	return 0;
 }
 
-/*! \brief Build contact header - the contact header we send out */
-static void build_contact(struct sip_pvt *p)
+/*!
+ * \brief Determine if, as a UAC, we need to use a SIPS Contact.
+ *
+ * This uses the rules defined in RFC 3621 section 8.1.1.8 to
+ * determine if a SIPS URI should be used as the Contact header
+ * on our outgoing request.
+ *
+ * \param req The outgoing SIP request
+ * \retval 0 SIPS is not required
+ * \retval 1 SIPS is required
+ */
+static int uac_sips_contact(struct sip_request *req)
+{
+	const char *route = sip_get_header(req, "Route");
+
+	if (!strncmp(REQ_OFFSET_TO_STR(req, rlpart2), "sips:", 5)) {
+		return 1;
+	}
+
+	if (route) {
+		char *route_uri = get_in_brackets(ast_strdupa(route));
+
+		if (!strncmp(route_uri, "sips:", 5)) {
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+/*!
+ * \brief Build contact header
+ *
+ * This is the Contact header that we send out in SIP requests and responses
+ * involving this sip_pvt.
+ *
+ * The incoming parameter is used to tell if we are building the request parameter
+ * is an incoming SIP request that we are building the Contact header in response to,
+ * or if the req parameter is an outbound SIP request that we will later be adding
+ * the Contact header to.
+ *
+ * \param p The sip_pvt where the built Contact will be saved.
+ * \param req The request that triggered the creation of a Contact header.
+ * \param incoming Indicates if the Contact header is being created for a response to an incoming request
+ */
+static void build_contact(struct sip_pvt *p, struct sip_request *req, int incoming)
 {
 	char tmp[SIPBUFSIZE];
 	char *user = ast_uri_encode(p->exten, tmp, sizeof(tmp), ast_uri_sip_user);
+	int use_sips;
+
+	if (incoming) {
+		use_sips = uas_sips_contact(req);
+	} else {
+		use_sips = uac_sips_contact(req);
+	}
 
 	if (p->socket.type == AST_TRANSPORT_UDP) {
-		ast_string_field_build(p, our_contact, "<sip:%s%s%s>", user,
-			ast_strlen_zero(user) ? "" : "@", ast_sockaddr_stringify_remote(&p->ourip));
+		ast_string_field_build(p, our_contact, "<%s:%s%s%s>", use_sips ? "sips" : "sip",
+			user, ast_strlen_zero(user) ? "" : "@",
+			ast_sockaddr_stringify_remote(&p->ourip));
 	} else {
-		ast_string_field_build(p, our_contact, "<sip:%s%s%s;transport=%s>", user,
-			ast_strlen_zero(user) ? "" : "@", ast_sockaddr_stringify_remote(&p->ourip),
-			sip_get_transport(p->socket.type));
+		ast_string_field_build(p, our_contact, "<%s:%s%s%s;transport=%s>",
+			use_sips ? "sips" : "sip", user, ast_strlen_zero(user) ? "" : "@",
+			ast_sockaddr_stringify_remote(&p->ourip), sip_get_transport(p->socket.type));
 	}
 }
 
@@ -13864,6 +13972,7 @@ static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmetho
 	int cid_has_name = 1;
 	int cid_has_num = 1;
 	struct ast_party_id connected_id;
+	int ret;
 
 	if (ast_test_flag(&p->flags[0], SIP_USEREQPHONE)) {
 	 	const char *s = p->username;	/* being a string field, cannot be NULL */
@@ -13948,26 +14057,41 @@ static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmetho
 
 	ast_copy_string(tmp_l, l, sizeof(tmp_l));
 	if (sip_cfg.pedanticsipchecking) {
-		ast_escape_quoted(n, tmp_n, sizeof(tmp_n));
-		n = tmp_n;
 		ast_uri_encode(l, tmp_l, sizeof(tmp_l), ast_uri_sip_user);
 	}
 
 	ourport = (p->fromdomainport && (p->fromdomainport != STANDARD_SIP_PORT)) ? p->fromdomainport : ast_sockaddr_port(&p->ourip);
 
-	/* If a caller id name was specified, add a display name. */
-	if (cid_has_name || !cid_has_num) {
-		snprintf(from, sizeof(from), "\"%s\" ", n);
+	if (!sip_standard_port(p->socket.type, ourport)) {
+		ret = snprintf(from, sizeof(from), "<sip:%s@%s:%d>;tag=%s", tmp_l, d, ourport, p->tag);
 	} else {
-		from[0] = '\0';
+		ret = snprintf(from, sizeof(from), "<sip:%s@%s>;tag=%s", tmp_l, d, p->tag);
+	}
+	if (ret < 0 || ret >= sizeof(from)) { /* a return value of size or more means that the output was truncated */
+		/* We don't have an escape path from here... */
+		ast_log(LOG_ERROR, "The From header was truncated in call '%s'. This call setup will fail.\n", p->callid);
 	}
 
-	if (!sip_standard_port(p->socket.type, ourport)) {
-		size_t offset = strlen(from);
-		snprintf(&from[offset], sizeof(from) - offset, "<sip:%s@%s:%d>;tag=%s", tmp_l, d, ourport, p->tag);
-	} else {
-		size_t offset = strlen(from);
-		snprintf(&from[offset], sizeof(from) - offset, "<sip:%s@%s>;tag=%s", tmp_l, d, p->tag);
+	/* If a caller id name was specified, prefix a display name, if there is enough room. */
+	if (cid_has_name || !cid_has_num) {
+		size_t written = strlen(from);
+		ssize_t left = sizeof(from) - written - 4; /* '"" \0' */
+		if (left > 0) {
+			size_t name_len;
+			if (sip_cfg.pedanticsipchecking) {
+				ast_escape_quoted(n, tmp_n, MIN(left + 1, sizeof(tmp_n)));
+				n = tmp_n;
+			}
+			name_len = strlen(n);
+			if (left < name_len) {
+				name_len = left;
+			}
+			memmove(from + name_len + 3, from, written + 1);
+			from[0] = '"';
+			memcpy(from + 1, n, name_len);
+			from[name_len + 1] = '"';
+			from[name_len + 2] = ' ';
+		}
 	}
 
 	if (!ast_strlen_zero(explicit_uri)) {
@@ -14009,21 +14133,25 @@ static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmetho
  		/*! \todo Need to add back the VXML URL here at some point, possibly use build_string for all this junk */
  		if (!strchr(p->todnid, '@')) {
  			/* We have no domain in the dnid */
- 			snprintf(to, sizeof(to), "<sip:%s@%s>%s%s", p->todnid, p->tohost, ast_strlen_zero(p->theirtag) ? "" : ";tag=", p->theirtag);
+			ret = snprintf(to, sizeof(to), "<sip:%s@%s>%s%s", p->todnid, p->tohost, ast_strlen_zero(p->theirtag) ? "" : ";tag=", p->theirtag);
  		} else {
- 			snprintf(to, sizeof(to), "<sip:%s>%s%s", p->todnid, ast_strlen_zero(p->theirtag) ? "" : ";tag=", p->theirtag);
+			ret = snprintf(to, sizeof(to), "<sip:%s>%s%s", p->todnid, ast_strlen_zero(p->theirtag) ? "" : ";tag=", p->theirtag);
  		}
  	} else {
  		if (sipmethod == SIP_NOTIFY && !ast_strlen_zero(p->theirtag)) {
  			/* If this is a NOTIFY, use the From: tag in the subscribe (RFC 3265) */
-			snprintf(to, sizeof(to), "<%s%s>;tag=%s", (strncasecmp(p->uri, "sip:", 4) ? "sip:" : ""), p->uri, p->theirtag);
+			ret = snprintf(to, sizeof(to), "<%s%s>;tag=%s", (strncasecmp(p->uri, "sip:", 4) ? "sip:" : ""), p->uri, p->theirtag);
  		} else if (p->options && p->options->vxml_url) {
  			/* If there is a VXML URL append it to the SIP URL */
- 			snprintf(to, sizeof(to), "<%s>;%s", p->uri, p->options->vxml_url);
+			ret = snprintf(to, sizeof(to), "<%s>;%s", p->uri, p->options->vxml_url);
  		} else {
- 			snprintf(to, sizeof(to), "<%s>", p->uri);
+			ret = snprintf(to, sizeof(to), "<%s>", p->uri);
 		}
  	}
+	if (ret < 0 || ret >= sizeof(to)) { /* a return value of size or more means that the output was truncated */
+		/* We don't have an escape path from here... */
+		ast_log(LOG_ERROR, "The To header was truncated in call '%s'. This call setup will fail.\n", p->callid);
+	}
 
 	init_req(req, sipmethod, p->uri);
 	/* now tmp_n is available so reuse it to build the CSeq */
@@ -14040,7 +14168,7 @@ static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmetho
 	add_header(req, "From", from);
 	add_header(req, "To", to);
 	ast_string_field_set(p, exten, l);
-	build_contact(p);
+	build_contact(p, req, 0);
 	add_header(req, "Contact", p->our_contact);
 	add_header(req, "Call-ID", p->callid);
 	add_header(req, "CSeq", tmp_n);
@@ -14469,7 +14597,6 @@ static int __sip_subscribe_mwi_do(struct sip_subscription_mwi *mwi)
 	set_socket_transport(&mwi->call->socket, mwi->transport);
 	mwi->call->socket.port = htons(mwi->portno);
 	ast_sip_ouraddrfor(&mwi->call->sa, &mwi->call->ourip, mwi->call);
-	build_contact(mwi->call);
 	build_via(mwi->call);
 
 	/* Change the dialog callid. */
@@ -15437,7 +15564,6 @@ static int transmit_register(struct sip_registry *r, int sipmethod, const char *
 		  internal network so we can register through nat
 		 */
 		ast_sip_ouraddrfor(&p->sa, &p->ourip, p);
-		build_contact(p);
 	}
 
 	/* set up a timeout */
@@ -15517,6 +15643,7 @@ static int transmit_register(struct sip_registry *r, int sipmethod, const char *
 	}
 
 	add_expires(&req, r->expiry);
+	build_contact(p, &req, 0);
 	add_header(&req, "Contact", p->our_contact);
 
 	initialize_initreq(p, &req);
@@ -15804,7 +15931,7 @@ static void destroy_association(struct sip_peer *peer)
 
 	if (!sip_cfg.ignore_regexpire) {
 		if (peer->rt_fromcontact && sip_cfg.peer_rtupdate) {
-			ast_update_realtime(tablename, "name", peer->name, "fullcontact", "", "ipaddr", "", "port", "", "regseconds", "0", "regserver", "", "useragent", "", "lastms", "0", SENTINEL);
+			ast_update_realtime(tablename, "name", peer->name, "fullcontact", "", "ipaddr", "", "port", "0", "regseconds", "0", "regserver", "", "useragent", "", "lastms", "0", SENTINEL);
 		} else {
 			ast_db_del("SIP/Registry", peer->name);
 			ast_db_del("SIP/RegistryPath", peer->name);
@@ -15915,6 +16042,17 @@ static int sip_poke_peer_s(const void *data)
 	return 0;
 }
 
+static int sip_poke_peer_now(const void *data)
+{
+	struct sip_peer *peer = (struct sip_peer *) data;
+
+	peer->pokeexpire = -1;
+	sip_poke_peer(peer, 0);
+	sip_unref_peer(peer, "removing poke peer ref");
+
+	return 0;
+}
+
 /*! \brief Get registration details from Asterisk DB */
 static void reg_source_db(struct sip_peer *peer)
 {
@@ -17046,7 +17184,7 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sock
 	}
 
 	ast_string_field_set(p, exten, name);
-	build_contact(p);
+	build_contact(p, req, 1);
 	if (req->ignore) {
 		/* Expires is a special case, where we only want to load the peer if this isn't a deregistration attempt */
 		const char *expires = sip_get_header(req, "Expires");
@@ -17067,8 +17205,7 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sock
 	/* If we don't want username disclosure, use the bogus_peer when a user
 	 * is not found. */
 	if (!peer && sip_cfg.alwaysauthreject && sip_cfg.autocreatepeer == AUTOPEERS_DISABLED) {
-		peer = bogus_peer;
-		sip_ref_peer(peer, "register_verify: ref the bogus_peer");
+		peer = ao2_t_global_obj_ref(g_bogus_peer, "register_verify: Get the bogus peer.");
 	}
 
 	if (!(peer && ast_apply_acl(peer->acl, addr, "SIP Peer ACL: "))) {
@@ -18321,6 +18458,7 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of,
 	enum check_auth_result res;
 	int debug = sip_debug_test_addr(addr);
 	struct sip_peer *peer;
+	struct sip_peer *bogus_peer;
 
 	if (sipmethod == SIP_SUBSCRIBE) {
 		/* For subscribes, match on device name only; for other methods,
@@ -18360,8 +18498,13 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of,
 		/* If you do mind, we use a peer that will never authenticate.
 		 * This ensures that we follow the same code path as regular
 		 * auth: less chance for username disclosure. */
-		peer = bogus_peer;
-		sip_ref_peer(peer, "sip_ref_peer: check_peer_ok: must ref bogus_peer so unreffing it does not fail");
+		peer = ao2_t_global_obj_ref(g_bogus_peer, "check_peer_ok: Get the bogus peer.");
+		if (!peer) {
+			return AUTH_DONT_KNOW;
+		}
+		bogus_peer = peer;
+	} else {
+		bogus_peer = NULL;
 	}
 
 	/*  build_peer, called through sip_find_peer, is not able to check the
@@ -18592,8 +18735,9 @@ static enum check_auth_result check_user_full(struct sip_pvt *p, struct sip_requ
 		if (t)
 			*t = '\0';
 
-		if (ast_strlen_zero(p->our_contact))
-			build_contact(p);
+		if (ast_strlen_zero(p->our_contact)) {
+			build_contact(p, req, 1);
+		}
 	}
 
 	of = get_in_brackets(of);
@@ -19032,12 +19176,12 @@ static const struct _map_x_s strefreshers[] = {
         { -1,                           NULL   },
 };
 
-static const char *strefresherparam2str(enum st_refresher r)
+static const char *strefresherparam2str(enum st_refresher_param r)
 {
 	return map_x_s(strefresher_params, r, "Unknown");
 }
 
-static enum st_refresher str2strefresherparam(const char *s)
+static enum st_refresher_param str2strefresherparam(const char *s)
 {
 	return map_s_x(strefresher_params, s, -1);
 }
@@ -19240,12 +19384,8 @@ static int manager_show_registry(struct mansession *s, const struct message *m)
 	}
 	ao2_iterator_destroy(&iter);
 
-	astman_append(s,
-		"Event: RegistrationsComplete\r\n"
-		"EventList: Complete\r\n"
-		"ListItems: %d\r\n"
-		"%s"
-		"\r\n", total, idtext);
+	astman_send_list_complete_start(s, m, "RegistrationsComplete", total);
+	astman_send_list_complete_end(s);
 
 	return 0;
 }
@@ -19263,15 +19403,13 @@ static int manager_sip_show_peers(struct mansession *s, const struct message *m)
 		snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
 
 	astman_send_listack(s, m, "Peer status list will follow", "start");
+
 	/* List the peers in separate manager events */
 	_sip_show_peers(-1, &total, s, m, 3, a);
+
 	/* Send final confirmation */
-	astman_append(s,
-	"Event: PeerlistComplete\r\n"
-	"EventList: Complete\r\n"
-	"ListItems: %d\r\n"
-	"%s"
-	"\r\n", total, idtext);
+	astman_send_list_complete_start(s, m, "PeerlistComplete", total);
+	astman_send_list_complete_end(s);
 	return 0;
 }
 
@@ -19676,21 +19814,20 @@ static void cleanup_stale_contexts(char *new, char *old)
 	char *oldcontext, *newcontext, *stalecontext, *stringp, newlist[AST_MAX_CONTEXT];
 
 	while ((oldcontext = strsep(&old, "&"))) {
-		stalecontext = '\0';
+		stalecontext = NULL;
 		ast_copy_string(newlist, new, sizeof(newlist));
 		stringp = newlist;
 		while ((newcontext = strsep(&stringp, "&"))) {
 			if (!strcmp(newcontext, oldcontext)) {
 				/* This is not the context you're looking for */
-				stalecontext = '\0';
+				stalecontext = NULL;
 				break;
 			} else if (strcmp(newcontext, oldcontext)) {
 				stalecontext = oldcontext;
 			}
 
 		}
-		if (stalecontext)
-			ast_context_destroy(ast_context_find(stalecontext), "SIP");
+		ast_context_destroy_by_name(stalecontext, "SIP");
 	}
 }
 
@@ -20034,9 +20171,11 @@ static int manager_sip_peer_status(struct mansession *s, const struct message *m
 {
 	const char *id = astman_get_header(m,"ActionID");
 	const char *peer_name = astman_get_header(m,"Peer");
-	char idText[256] = "";
+	char idText[256];
 	struct sip_peer *peer = NULL;
+	int num_peers = 0;
 
+	idText[0] = '\0';
 	if (!ast_strlen_zero(id)) {
 		snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
 	}
@@ -20054,15 +20193,17 @@ static int manager_sip_peer_status(struct mansession *s, const struct message *m
 		}
 	}
 
-	astman_send_ack(s, m, "Peer status will follow");
+	astman_send_listack(s, m, "Peer status will follow", "start");
 
 	if (!peer) {
 		struct ao2_iterator i = ao2_iterator_init(peers, 0);
+
 		while ((peer = ao2_t_iterator_next(&i, "iterate thru peers table for SIPpeerstatus"))) {
 			ao2_lock(peer);
 			send_manager_peer_status(s, peer, idText);
 			ao2_unlock(peer);
 			sip_unref_peer(peer, "unref peer for SIPpeerstatus");
+			++num_peers;
 		}
 		ao2_iterator_destroy(&i);
 	} else {
@@ -20070,14 +20211,11 @@ static int manager_sip_peer_status(struct mansession *s, const struct message *m
 		send_manager_peer_status(s, peer, idText);
 		ao2_unlock(peer);
 		sip_unref_peer(peer, "unref peer for SIPpeerstatus");
+		++num_peers;
 	}
 
-
-	astman_append(s,
-	"Event: SIPpeerstatusComplete\r\n"
-	"%s"
-	"\r\n",
-	idText);
+	astman_send_list_complete_start(s, m, "SIPpeerstatusComplete", num_peers);
+	astman_send_list_complete_end(s);
 
 	return 0;
 }
@@ -20196,7 +20334,7 @@ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct
 	char status[30] = "";
 	char cbuf[256];
 	struct sip_peer *peer;
-	struct ast_str *codec_buf = ast_str_alloca(64);
+	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 	struct ast_variable *v;
 	int x = 0, load_realtime;
 	int realtimepeers;
@@ -20596,24 +20734,33 @@ static char *sip_show_user(struct ast_cli_entry *e, int cmd, struct ast_cli_args
 static char *sip_show_sched(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
 	struct ast_str *cbuf;
-	struct ast_cb_names cbnames = {9, { "retrans_pkt",
-                                        "__sip_autodestruct",
-                                        "expire_register",
-                                        "auto_congest",
-                                        "sip_reg_timeout",
-                                        "sip_poke_peer_s",
-                                        "sip_poke_noanswer",
-                                        "sip_reregister",
-                                        "sip_reinvite_retry"},
-								   { retrans_pkt,
-                                     __sip_autodestruct,
-                                     expire_register,
-                                     auto_congest,
-                                     sip_reg_timeout,
-                                     sip_poke_peer_s,
-                                     sip_poke_noanswer,
-                                     sip_reregister,
-                                     sip_reinvite_retry}};
+	struct ast_cb_names cbnames = {
+		10,
+		{
+			"retrans_pkt",
+			"__sip_autodestruct",
+			"expire_register",
+			"auto_congest",
+			"sip_reg_timeout",
+			"sip_poke_peer_s",
+			"sip_poke_peer_now",
+			"sip_poke_noanswer",
+			"sip_reregister",
+			"sip_reinvite_retry"
+		},
+		{
+			retrans_pkt,
+			__sip_autodestruct,
+			expire_register,
+			auto_congest,
+			sip_reg_timeout,
+			sip_poke_peer_s,
+			sip_poke_peer_now,
+			sip_poke_noanswer,
+			sip_reregister,
+			sip_reinvite_retry
+		}
+	};
 
 	switch (cmd) {
 	case CLI_INIT:
@@ -20718,7 +20865,7 @@ static char *sip_unregister(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
 		return CLI_SHOWUSAGE;
 
 	if ((peer = sip_find_peer(a->argv[2], NULL, load_realtime, FINDPEERS, TRUE, 0))) {
-		if (peer->expire > 0) {
+		if (peer->expire > -1) {
 			AST_SCHED_DEL_UNREF(sched, peer->expire,
 				sip_unref_peer(peer, "remove register expire ref"));
 			expire_register(sip_ref_peer(peer, "ref for expire_register"));
@@ -20833,7 +20980,7 @@ static char *sip_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_
 {
 	int realtimepeers;
 	int realtimeregs;
-	struct ast_str *codec_buf = ast_str_alloca(64);
+	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 	const char *msg;	/* temporary msg pointer */
 	struct sip_auth_container *credentials;
 
@@ -21150,7 +21297,7 @@ static int show_channels_cb(void *__cur, void *__arg, int flags)
 	if (cur->subscribed == NONE && !arg->subscriptions) {
 		/* set if SIP transfer in progress */
 		const char *referstatus = cur->refer ? referstatus2str(cur->refer->status) : "";
-		struct ast_str *codec_buf = ast_str_alloca(64);
+		struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 		ast_cli(arg->fd, FORMAT, ast_sockaddr_stringify_addr(dst),
 				S_OR(cur->username, S_OR(cur->cid_num, "(None)")),
@@ -21297,7 +21444,7 @@ static char *complete_sip_registered_peer(const char *word, int state, int flags
        while ((peer = ao2_t_iterator_next(&i, "iterate thru peers table"))) {
 	       if (!strncasecmp(word, peer->name, wordlen) &&
 		   (!flags2 || ast_test_flag(&peer->flags[1], flags2)) &&
-		   ++which > state && peer->expire > 0)
+		   ++which > state && peer->expire > -1)
 		       result = ast_strdup(peer->name);
 	       if (result) {
 		       sip_unref_peer(peer, "toss iterator peer ptr before break");
@@ -21396,7 +21543,7 @@ static char *sip_show_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_a
 
 		if (!strncasecmp(cur->callid, a->argv[3], len)) {
 			struct ast_str *strbuf;
-			struct ast_str *codec_buf = ast_str_alloca(64);
+			struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 			ast_cli(a->fd, "\n");
 			if (cur->subscribed != NONE) {
@@ -21474,7 +21621,7 @@ static char *sip_show_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_a
  					ast_cli(a->fd, "  S-Timer Peer Sts:       %s\n", cur->stimer->st_active_peer_ua ? "Active" : "Inactive");
  					ast_cli(a->fd, "  S-Timer Cached Min-SE:  %d\n", cur->stimer->st_cached_min_se);
  					ast_cli(a->fd, "  S-Timer Cached SE:      %d\n", cur->stimer->st_cached_max_se);
- 					ast_cli(a->fd, "  S-Timer Cached Ref:     %s\n", strefresherparam2str(cur->stimer->st_cached_ref));
+ 					ast_cli(a->fd, "  S-Timer Cached Ref:     %s\n", strefresher2str(cur->stimer->st_cached_ref));
  					ast_cli(a->fd, "  S-Timer Cached Mode:    %s\n", stmode2str(cur->stimer->st_cached_mode));
  				}
 			}
@@ -22355,7 +22502,7 @@ static int function_sippeer(struct ast_channel *chan, const char *cmd, char *dat
 	} else  if (!strcasecmp(colname, "callerid_num")) {
 		ast_copy_string(buf, peer->cid_num, len);
 	} else  if (!strcasecmp(colname, "codecs")) {
-		struct ast_str *codec_buf = ast_str_alloca(64);
+		struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 		ast_format_cap_get_names(peer->caps, &codec_buf);
 		ast_copy_string(buf, ast_str_buffer(codec_buf), len);
 	} else if (!strcasecmp(colname, "encryption")) {
@@ -22629,7 +22776,7 @@ static void check_pendings(struct sip_pvt *p)
 		sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
 	} else if (ast_test_flag(&p->flags[0], SIP_NEEDREINVITE)) {
 		/* if we can't REINVITE, hold it for later */
-		if (p->pendinginvite || p->invitestate == INV_CALLING || p->invitestate == INV_PROCEEDING || p->invitestate == INV_EARLY_MEDIA || p->waitid > 0) {
+		if (p->pendinginvite || p->invitestate == INV_CALLING || p->invitestate == INV_PROCEEDING || p->invitestate == INV_EARLY_MEDIA || p->waitid > -1) {
 			ast_debug(2, "NOT Sending pending reinvite (yet) on '%s'\n", p->callid);
 		} else {
 			ast_debug(2, "Sending pending reinvite on '%s'\n", p->callid);
@@ -23066,7 +23213,7 @@ static void handle_response_invite(struct sip_pvt *p, int resp, const char *rest
 		} else if (!reinvite) {
 			struct ast_sockaddr remote_address = {{0,}};
 
-			ast_rtp_instance_get_remote_address(p->rtp, &remote_address);
+			ast_rtp_instance_get_requested_target_address(p->rtp, &remote_address);
 			if (ast_sockaddr_isnull(&remote_address) || (!ast_strlen_zero(p->theirprovtag) && strcmp(p->theirtag, p->theirprovtag))) {
 				ast_log(LOG_WARNING, "Received response: \"200 OK\" from '%s' without SDP\n", p->relatedpeer->name);
 				ast_set_flag(&p->flags[0], SIP_PENDINGBYE);
@@ -23585,7 +23732,7 @@ static void handle_response_refer(struct sip_pvt *p, int resp, const char *rest,
 		   theoretically possible. */
 
 		if (resp < 299) { /* 1xx cases don't get here */
-			ast_log(LOG_WARNING, "SIP transfer to %s had unxpected 2xx response (%d), confusion is possible. \n", p->refer->refer_to, resp);
+			ast_log(LOG_WARNING, "SIP transfer to %s had unexpected 2xx response (%d), confusion is possible. \n", p->refer->refer_to, resp);
 		} else {
 			ast_log(LOG_WARNING, "SIP transfer to %s with response (%d). \n", p->refer->refer_to, resp);
 		}
@@ -24432,10 +24579,9 @@ static void *sip_pickup_thread(void *stuff)
 	struct ast_channel *chan;
 	chan = stuff;
 
+	ast_channel_hangupcause_set(chan, AST_CAUSE_NORMAL_CLEARING);
 	if (ast_pickup_call(chan)) {
 		ast_channel_hangupcause_set(chan, AST_CAUSE_CALL_REJECTED);
-	} else {
-		ast_channel_hangupcause_set(chan, AST_CAUSE_NORMAL_CLEARING);
 	}
 	ast_hangup(chan);
 	ast_channel_unref(chan);
@@ -24743,12 +24889,16 @@ static int handle_request_options(struct sip_pvt *p, struct sip_request *req, st
 
 	/* must go through authentication before getting here */
 	gotdest = get_destination(p, req, NULL);
-	build_contact(p);
+	build_contact(p, req, 1);
 
 	if (ast_strlen_zero(p->context))
 		ast_string_field_set(p, context, sip_cfg.default_context);
 
 	if (ast_shutting_down()) {
+		/*
+		 * Not taking any new calls at this time.
+		 * Likely a server availability OPTIONS poll.
+		 */
 		msg = "503 Unavailable";
 	} else {
 		msg = "404 Not Found";
@@ -24835,10 +24985,12 @@ static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req,
 		if (ast_bridge_impart(bridge, c, replaces_chan, NULL,
 			AST_BRIDGE_IMPART_CHAN_INDEPENDENT)) {
 			ast_hangup(c);
+			ast_channel_unref(c);
 		}
 	} else {
 		ast_channel_move(replaces_chan, c);
 		ast_hangup(c);
+		ast_channel_unref(c);
 	}
 	sip_pvt_lock(p);
 	return 0;
@@ -25509,8 +25661,8 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, str
 			goto request_invite_cleanup;
 		}
 		gotdest = get_destination(p, NULL, &cc_recall_core_id);	/* Get destination right away */
-		extract_uri(p, req);			/* Get the Contact URI */
-		build_contact(p);			/* Build our contact header */
+		extract_uri(p, req);        /* Get the Contact URI */
+		build_contact(p, req, 1);   /* Build our contact header */
 
 		if (p->rtp) {
 			ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_DTMF, ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833);
@@ -25710,11 +25862,13 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, str
 
 				switch(result) {
 				case AST_PBX_FAILED:
+					sip_alreadygone(p);
 					ast_log(LOG_WARNING, "Failed to start PBX :(\n");
 					p->invitestate = INV_COMPLETED;
 					transmit_response_reliable(p, "503 Unavailable", req);
 					break;
 				case AST_PBX_CALL_LIMIT:
+					sip_alreadygone(p);
 					ast_log(LOG_WARNING, "Failed to start PBX (call limit reached) \n");
 					p->invitestate = INV_COMPLETED;
 					transmit_response_reliable(p, "480 Temporarily Unavailable", req);
@@ -27418,7 +27572,7 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
 
 	/* Get full contact header - this needs to be used as a request URI in NOTIFY's */
 	parse_ok_contact(p, req);
-	build_contact(p);
+	build_contact(p, req, 1);
 
 	/* Initialize tag for new subscriptions */
 	if (ast_strlen_zero(p->tag)) {
@@ -28009,7 +28163,7 @@ static int handle_incoming(struct sip_pvt *p, struct sip_request *req, struct as
 		res = handle_request_invite(p, req, addr, seqno, recount, e, nounlock);
 
 		if (res < 9) {
-			sip_report_security_event(p, req, res);
+			sip_report_security_event(NULL, &p->recv, p, req, res);
 		}
 
 		switch (res) {
@@ -28048,7 +28202,7 @@ static int handle_incoming(struct sip_pvt *p, struct sip_request *req, struct as
 		break;
 	case SIP_REGISTER:
 		res = handle_request_register(p, req, addr, e);
-		sip_report_security_event(p, req, res);
+		sip_report_security_event(p->exten, NULL, p, req, res);
 		break;
 	case SIP_INFO:
 		if (req->debug)
@@ -28689,7 +28843,8 @@ static int check_rtp_timeout(struct sip_pvt *dialog, time_t t)
 					ast_channel_name(dialog->owner), (long) (t - dialog->lastrtprx));
 				send_session_timeout(dialog->owner, "RTPTimeout");
 
-				/* Issue a softhangup */
+				/* Issue a softhangup - cause 44 (as used by Cisco for RTP timeouts) */
+				ast_channel_hangupcause_set(dialog->owner, AST_CAUSE_REQUESTED_CHAN_UNAVAIL);
 				ast_softhangup_nolock(dialog->owner, AST_SOFTHANGUP_DEV);
 				ast_channel_unlock(dialog->owner);
 				/* forget the timeouts for this call, since a hangup
@@ -28806,7 +28961,7 @@ static int restart_monitor(void)
 		ast_log(LOG_WARNING, "Cannot kill myself\n");
 		return -1;
 	}
-	if (monitor_thread != AST_PTHREADT_NULL) {
+	if (monitor_thread != AST_PTHREADT_NULL && monitor_thread != AST_PTHREADT_STOP) {
 		/* Wake up the thread */
 		pthread_kill(monitor_thread, SIGURG);
 	} else {
@@ -29490,8 +29645,8 @@ static struct ast_channel *sip_request_call(const char *type, struct ast_format_
 	struct ast_channel *tmpc = NULL;
 	char *ext = NULL, *host;
 	char tmp[256];
-	struct ast_str *codec_buf = ast_str_alloca(64);
-	struct ast_str *cap_buf = ast_str_alloca(64);
+	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+	struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 	char *dnid;
 	char *secret = NULL;
 	char *md5secret = NULL;
@@ -29883,8 +30038,8 @@ static int handle_common_options(struct ast_flags *flags, struct ast_flags *mask
 		ast_clear_flag(&flags[0], SIP_PROG_INBAND);
 		if (ast_true(v->value))
 			ast_set_flag(&flags[0], SIP_PROG_INBAND_YES);
-		else if (strcasecmp(v->value, "never"))
-			ast_set_flag(&flags[0], SIP_PROG_INBAND_NO);
+		else if (!strcasecmp(v->value, "never"))
+			ast_set_flag(&flags[0], SIP_PROG_INBAND_NEVER);
 	} else if (!strcasecmp(v->name, "promiscredir")) {
 		ast_set_flag(&mask[0], SIP_PROMISCREDIR);
 		ast_set2_flag(&flags[0], ast_true(v->value), SIP_PROMISCREDIR);
@@ -30145,13 +30300,11 @@ static struct ast_variable *add_var(const char *buf, struct ast_variable *list)
 /*! \brief Set peer defaults before configuring specific configurations */
 static void set_peer_defaults(struct sip_peer *peer)
 {
-	if (peer->expire == 0) {
+	if (peer->expire < 0) {
 		/* Don't reset expire or port time during reload
 		   if we have an active registration
 		*/
-		peer->expire = -1;
-		peer->pokeexpire = -1;
-		peer->keepalivesend = -1;
+		peer_sched_cleanup(peer);
 		set_socket_transport(&peer->socket, AST_TRANSPORT_UDP);
 	}
 	peer->type = SIP_TYPE_PEER;
@@ -30236,6 +30389,10 @@ static struct sip_peer *temp_peer(const char *name)
 	}
 
 	ast_atomic_fetchadd_int(&apeerobjs, 1);
+	peer->expire = -1;
+	peer->pokeexpire = -1;
+	peer->keepalivesend = -1;
+
 	set_peer_defaults(peer);
 
 	ast_copy_string(peer->name, name, sizeof(peer->name));
@@ -30325,8 +30482,11 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
 		if (!ast_sockaddr_isnull(&peer->addr)) {
 			ao2_t_unlink(peers_by_ip, peer, "ao2_unlink peer from peers_by_ip table");
 		}
-		if (!(peer->the_mark))
+		if (!(peer->the_mark)) {
 			firstpass = 0;
+		} else {
+			ast_format_cap_remove_by_type(peer->caps, AST_MEDIA_TYPE_UNKNOWN);
+		}
 	} else {
 		if (!(peer = ao2_t_alloc(sizeof(*peer), sip_destroy_peer_fn, "allocate a peer struct"))) {
 			return NULL;
@@ -30353,6 +30513,10 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
 			ast_debug(3, "-REALTIME- peer built. Name: %s. Peer objects: %d\n", name, rpeerobjs);
 		} else
 			ast_atomic_fetchadd_int(&speerobjs, 1);
+
+		peer->expire = -1;
+		peer->pokeexpire = -1;
+		peer->keepalivesend = -1;
 	}
 
 	/* Note that our peer HAS had its reference count increased */
@@ -30575,7 +30739,7 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
 				if (peer->callingpres == -1) {
 					peer->callingpres = atoi(v->value);
 				}
-			} else if (!strcasecmp(v->name, "username") || !strcmp(v->name, "defaultuser")) {	/* "username" is deprecated */
+			} else if (!strcasecmp(v->name, "username") || !strcasecmp(v->name, "defaultuser")) {	/* "username" is deprecated */
 				ast_string_field_set(peer, username, v->value);
 				if (!strcasecmp(v->name, "username")) {
 					if (deprecation_warning) {
@@ -30986,7 +31150,17 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
 
 		/* Startup regular pokes */
 		if (!devstate_only && enablepoke) {
-			sip_poke_peer(peer, 0);
+			/*
+			 * We cannot poke the peer now in this thread without
+			 * a lock inversion so pass it off to the scheduler
+			 * thread.
+			 */
+			AST_SCHED_REPLACE_UNREF(peer->pokeexpire, sched,
+				0, /* Poke the peer ASAP */
+				sip_poke_peer_now, peer,
+				sip_unref_peer(_data, "removing poke peer ref"),
+				sip_unref_peer(peer, "removing poke peer ref"),
+				sip_ref_peer(peer, "adding poke peer ref"));
 		}
 	}
 
@@ -31072,38 +31246,37 @@ static void display_nat_warning(const char *cat, int reason, struct ast_flags *f
 	}
 }
 
-static void cleanup_all_regs(void)
+static int cleanup_registration(void *obj, void *arg, int flags)
 {
-	struct ao2_iterator iter;
-	struct sip_registry *iterator;
-	/* First, destroy all outstanding registry calls */
-	/* This is needed, since otherwise active registry entries will not be destroyed */
-	iter = ao2_iterator_init(registry_list, 0);
-	while ((iterator = ao2_t_iterator_next(&iter, "cleanup_all_regs iter"))) {
-		ao2_lock(iterator);
-
-		if (iterator->call) {
-			ast_debug(3, "Destroying active SIP dialog for registry %s@%s\n", iterator->username, iterator->hostname);
-			/* This will also remove references to the registry */
-			dialog_unlink_all(iterator->call);
-			iterator->call = dialog_unref(iterator->call, "remove iterator->call from registry traversal");
-		}
-		if (iterator->expire > -1) {
-			AST_SCHED_DEL_UNREF(sched, iterator->expire, ao2_t_ref(iterator, -1, "reg ptr unref from reload config"));
-		}
-		if (iterator->timeout > -1) {
-			AST_SCHED_DEL_UNREF(sched, iterator->timeout, ao2_t_ref(iterator, -1, "reg ptr unref from reload config"));
-		}
-		if (iterator->dnsmgr) {
-			ast_dnsmgr_release(iterator->dnsmgr);
-			iterator->dnsmgr = NULL;
-			ao2_t_ref(iterator, -1, "reg ptr unref from dnsmgr");
-		}
+	struct sip_registry *reg = obj;
+	ao2_lock(reg);
 
-		ao2_unlock(iterator);
-		ao2_t_ref(iterator, -1, "cleanup_all_regs iter");
+	if (reg->call) {
+		ast_debug(3, "Destroying active SIP dialog for registry %s@%s\n", reg->username, reg->hostname);
+		/* This will also remove references to the registry */
+		dialog_unlink_all(reg->call);
+		reg->call = dialog_unref(reg->call, "remove iterator->call from registry traversal");
 	}
-	ao2_iterator_destroy(&iter);
+	if (reg->expire > -1) {
+		AST_SCHED_DEL_UNREF(sched, reg->expire, ao2_t_ref(reg, -1, "reg ptr unref from reload config"));
+	}
+	if (reg->timeout > -1) {
+		AST_SCHED_DEL_UNREF(sched, reg->timeout, ao2_t_ref(reg, -1, "reg ptr unref from reload config"));
+	}
+	if (reg->dnsmgr) {
+		ast_dnsmgr_release(reg->dnsmgr);
+		reg->dnsmgr = NULL;
+		ao2_t_ref(reg, -1, "reg ptr unref from dnsmgr");
+	}
+
+	ao2_unlock(reg);
+	return CMP_MATCH;
+}
+
+static void cleanup_all_regs(void)
+{
+	ao2_t_callback(registry_list, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE,
+		cleanup_registration, NULL, "remove all SIP registry items");
 }
 
 /*! \brief Re-read SIP.conf config file
@@ -31130,6 +31303,7 @@ static int reload_config(enum channelreloadreason reason)
 	int bindport = 0;
 	int acl_change_subscription_needed = 0;
 	int min_subexpiry_set = 0, max_subexpiry_set = 0;
+	int websocket_was_enabled = sip_cfg.websocket_enabled;
 
 	run_start = time(0);
 	ast_unload_realtime("sipregs");
@@ -31297,6 +31471,7 @@ static int reload_config(enum channelreloadreason reason)
 	sip_cfg.peer_rtupdate = TRUE;
 	global_dynamic_exclude_static = 0;	/* Exclude static peers */
 	sip_cfg.tcp_enabled = FALSE;
+	sip_cfg.websocket_enabled = TRUE;
 
 	/* Session-Timers */
 	global_st_mode = SESSION_TIMER_MODE_ACCEPT;
@@ -31916,6 +32091,8 @@ static int reload_config(enum channelreloadreason reason)
 				ast_log(LOG_WARNING, "'%s' is not a valid websocket_write_timeout value at line %d. Using default '%d'.\n", v->value, v->lineno, AST_DEFAULT_WEBSOCKET_WRITE_TIMEOUT);
 				sip_cfg.websocket_write_timeout = AST_DEFAULT_WEBSOCKET_WRITE_TIMEOUT;
 			}
+		} else if (!strcasecmp(v->name, "websocket_enabled")) {
+			sip_cfg.websocket_enabled = ast_true(v->value);
 		}
 	}
 
@@ -32066,6 +32243,12 @@ static int reload_config(enum channelreloadreason reason)
 		ast_log(LOG_ERROR, "SIP TCP Server start failed. Not listening on TCP socket.\n");
 	} else {
 		ast_debug(2, "SIP TCP server started\n");
+		if (sip_tcp_desc.accept_fd >= 0) {
+			int flags = 1;
+			if (setsockopt(sip_tcp_desc.accept_fd, SOL_SOCKET, SO_KEEPALIVE, &flags, sizeof(flags))) {
+				ast_log(LOG_ERROR, "Error enabling TCP keep-alive on sip socket: %s\n", strerror(errno));
+			}
+		}
 	}
 
 	/* Start TLS server if needed */
@@ -32086,6 +32269,13 @@ static int reload_config(enum channelreloadreason reason)
 			ast_log(LOG_ERROR, "TLS Server start failed. Not listening on TLS socket.\n");
 			sip_tls_desc.tls_cfg = NULL;
 		}
+		if (sip_tls_desc.accept_fd >= 0) {
+			int flags = 1;
+			if (setsockopt(sip_tls_desc.accept_fd, SOL_SOCKET, SO_KEEPALIVE, &flags, sizeof(flags))) {
+				ast_log(LOG_ERROR, "Error enabling TCP keep-alive on sip socket: %s\n", strerror(errno));
+				sip_tls_desc.tls_cfg = NULL;
+			}
+		}
 	} else if (sip_tls_desc.tls_cfg->enabled) {
 		sip_tls_desc.tls_cfg = NULL;
 		ast_log(LOG_WARNING, "SIP TLS server did not load because of errors.\n");
@@ -32261,6 +32451,15 @@ static int reload_config(enum channelreloadreason reason)
 		notify_types = NULL;
 	}
 
+	/* If the module is loading it's not time to enable websockets yet. */
+	if (reason != CHANNEL_MODULE_LOAD && websocket_was_enabled != sip_cfg.websocket_enabled) {
+		if (sip_cfg.websocket_enabled) {
+			ast_websocket_add_protocol("sip", sip_websocket_callback);
+		} else {
+			ast_websocket_remove_protocol("sip", sip_websocket_callback);
+		}
+	}
+
 	run_end = time(0);
 	ast_debug(4, "SIP reload_config done...Runtime= %d sec\n", (int)(run_end-run_start));
 
@@ -32295,7 +32494,7 @@ static int sip_allow_anyrtp_remote(struct ast_channel *chan1, struct ast_rtp_ins
 	if (ast_test_flag(&p->flags[0], SIP_DIRECT_MEDIA)) {
 		struct ast_sockaddr us = { { 0, }, }, them = { { 0, }, };
 
-		ast_rtp_instance_get_remote_address(instance, &them);
+		ast_rtp_instance_get_requested_target_address(instance, &them);
 		ast_rtp_instance_get_local_address(instance, &us);
 
 		if (ast_apply_acl(acl, &them, "SIP Direct Media ACL: ") == AST_SENSE_DENY) {
@@ -32981,7 +33180,7 @@ static int sip_do_reload(enum channelreloadreason reason)
 /*! \brief Force reload of module from cli */
 static char *sip_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-	static struct sip_peer *tmp_peer, *new_peer;
+	static struct sip_peer *new_peer;
 
 	switch (cmd) {
 	case CLI_INIT:
@@ -33004,13 +33203,13 @@ static char *sip_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a
 	ast_mutex_unlock(&sip_reload_lock);
 	restart_monitor();
 
-	tmp_peer = bogus_peer;
 	/* Create new bogus peer possibly with new global settings. */
 	if ((new_peer = temp_peer("(bogus_peer)"))) {
 		ast_string_field_set(new_peer, md5secret, BOGUS_PEER_MD5SECRET);
 		ast_clear_flag(&new_peer->flags[0], SIP_INSECURE);
-		bogus_peer = new_peer;
-		ao2_t_ref(tmp_peer, -1, "unref the old bogus_peer during reload");
+		ao2_t_global_obj_replace_unref(g_bogus_peer, new_peer,
+			"Replacing the old bogus peer during reload.");
+		ao2_t_ref(new_peer, -1, "done with new_peer");
 	} else {
 		ast_log(LOG_ERROR, "Could not update the fake authentication peer.\n");
 		/* You probably have bigger (memory?) issues to worry about though.. */
@@ -33022,10 +33221,8 @@ static char *sip_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a
 /*! \brief  Part of Asterisk module interface */
 static int reload(void)
 {
-	if (sip_reload(0, 0, NULL)) {
-		return 0;
-	}
-	return 1;
+	sip_reload(0, 0, NULL);
+	return AST_MODULE_LOAD_SUCCESS;
 }
 
 /*! \brief  Return the first entry from ast_sockaddr_resolve filtered by address family
@@ -34263,6 +34460,8 @@ static int unload_module(void);
  */
 static int load_module(void)
 {
+	struct sip_peer *bogus_peer;
+
 	ast_verbose("SIP channel loading...\n");
 
 	if (STASIS_MESSAGE_TYPE_INIT(session_timeout_type)) {
@@ -34334,6 +34533,8 @@ static int load_module(void)
 	/* Make sure the auth will always fail. */
 	ast_string_field_set(bogus_peer, md5secret, BOGUS_PEER_MD5SECRET);
 	ast_clear_flag(&bogus_peer->flags[0], SIP_INSECURE);
+	ao2_t_global_obj_replace_unref(g_bogus_peer, bogus_peer, "Set the initial bogus peer.");
+	ao2_t_ref(bogus_peer, -1, "Module load is done with the bogus peer.");
 
 	/* Prepare the version that does not require DTMF BEGIN frames.
 	 * We need to use tricks such as memcpy and casts because the variable
@@ -34444,7 +34645,9 @@ static int load_module(void)
 	sip_register_tests();
 	network_change_stasis_subscribe();
 
-	ast_websocket_add_protocol("sip", sip_websocket_callback);
+	if (sip_cfg.websocket_enabled) {
+		ast_websocket_add_protocol("sip", sip_websocket_callback);
+	}
 
 	return AST_MODULE_LOAD_SUCCESS;
 }
@@ -34454,13 +34657,14 @@ static int unload_module(void)
 {
 	struct sip_pvt *p;
 	struct sip_threadinfo *th;
-	struct ast_context *con;
 	struct ao2_iterator i;
 	int wait_count;
 
 	ast_sip_api_provider_unregister();
 
-	ast_websocket_remove_protocol("sip", sip_websocket_callback);
+	if (sip_cfg.websocket_enabled) {
+		ast_websocket_remove_protocol("sip", sip_websocket_callback);
+	}
 
 	network_change_stasis_unsubscribe();
 	acl_change_event_stasis_unsubscribe();
@@ -34613,7 +34817,7 @@ static int unload_module(void)
 		ast_debug(2, "TCP/TLS thread container did not become empty :(\n");
 	}
 
-	ao2_t_cleanup(bogus_peer, "unref the bogus_peer");
+	ao2_t_global_obj_release(g_bogus_peer, "Release the bogus peer.");
 
 	ao2_t_cleanup(peers, "unref the peers table");
 	ao2_t_cleanup(peers_by_ip, "unref the peers_by_ip table");
@@ -34632,10 +34836,7 @@ static int unload_module(void)
 	close(sipsock);
 	io_context_destroy(io);
 	ast_sched_context_destroy(sched);
-	con = ast_context_find(used_context);
-	if (con) {
-		ast_context_destroy(con, "SIP");
-	}
+	ast_context_destroy_by_name(used_context, "SIP");
 	ast_unload_realtime("sipregs");
 	ast_unload_realtime("sippeers");
 	ast_cc_monitor_unregister(&sip_cc_monitor_callbacks);
diff --git a/channels/chan_skinny.c b/channels/chan_skinny.c
index 8317b89..5cdfe1b 100644
--- a/channels/chan_skinny.c
+++ b/channels/chan_skinny.c
@@ -39,7 +39,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428687 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/socket.h>
 #include <netinet/in.h>
@@ -1364,6 +1364,12 @@ static int gendigittimeout = 8000;
 /* How long to wait for an extra digit, if there is an ambiguous match */
 static int matchdigittimeout = 3000;
 
+/*!
+ * To apease the stupid compiler option on ast_sched_del()
+ * since we don't care about the return value.
+ */
+static int not_used;
+
 #define SUBSTATE_UNSET 0
 #define SUBSTATE_OFFHOOK 1
 #define SUBSTATE_ONHOOK 2
@@ -2185,21 +2191,20 @@ static void cleanup_stale_contexts(char *new, char *old)
 	char *oldcontext, *newcontext, *stalecontext, *stringp, newlist[AST_MAX_CONTEXT];
 
 	while ((oldcontext = strsep(&old, "&"))) {
-		stalecontext = '\0';
+		stalecontext = NULL;
 		ast_copy_string(newlist, new, sizeof(newlist));
 		stringp = newlist;
 		while ((newcontext = strsep(&stringp, "&"))) {
 			if (strcmp(newcontext, oldcontext) == 0) {
 				/* This is not the context you're looking for */
-				stalecontext = '\0';
+				stalecontext = NULL;
 				break;
 			} else if (strcmp(newcontext, oldcontext)) {
 				stalecontext = oldcontext;
 			}
 
 		}
-		if (stalecontext)
-			ast_context_destroy(ast_context_find(stalecontext), "Skinny");
+		ast_context_destroy_by_name(stalecontext, "Skinny");
 	}
 }
 
@@ -2263,10 +2268,10 @@ static int skinny_register(struct skinny_req *req, struct skinnysession *s)
 	int instance;
 	int res = -1;
 
-	if (s->auth_timeout_sched && ast_sched_del(sched, s->auth_timeout_sched)) {
-		return 0;
+	if (-1 < s->auth_timeout_sched) {
+		not_used = ast_sched_del(sched, s->auth_timeout_sched);
+		s->auth_timeout_sched = -1;
 	}
-	s->auth_timeout_sched = 0;
 
 	AST_LIST_LOCK(&devices);
 	AST_LIST_TRAVERSE(&devices, d, list){
@@ -2374,6 +2379,7 @@ static char *callstate2str(int ind)
 static int transmit_response_bysession(struct skinnysession *s, struct skinny_req *req)
 {
 	int res = 0;
+	unsigned long len;
 
 	if (!s) {
 		ast_log(LOG_WARNING, "Asked to transmit to a non-existent session!\n");
@@ -2382,7 +2388,10 @@ static int transmit_response_bysession(struct skinnysession *s, struct skinny_re
 
 	ast_mutex_lock(&s->lock);
 
-	if ((letohl(req->len) > SKINNY_MAX_PACKET) || (letohl(req->len) < 0)) {
+	/* Don't optimize out assigning letohl() to len. It is necessary to guarantee that the comparison will always catch invalid values.
+	 * letohl() may or may not return a signed value depending upon which definition is used. */
+	len = letohl(req->len);
+	if (SKINNY_MAX_PACKET < len) {
 		ast_log(LOG_WARNING, "transmit_response: the length of the request (%u) is out of bounds (%d)\n", letohl(req->len), SKINNY_MAX_PACKET);
 		ast_mutex_unlock(&s->lock);
 		return -1;
@@ -4135,24 +4144,17 @@ static char *_skinny_show_devices(int fd, int *total, struct mansession *s, cons
 /*    Inspired from chan_sip */
 static int manager_skinny_show_devices(struct mansession *s, const struct message *m)
 {
-	const char *id = astman_get_header(m, "ActionID");
 	const char *a[] = {"skinny", "show", "devices"};
-	char idtext[256] = "";
 	int total = 0;
 
-	if (!ast_strlen_zero(id))
-		snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
-
 	astman_send_listack(s, m, "Device status list will follow", "start");
+
 	/* List the devices in separate manager events */
 	_skinny_show_devices(-1, &total, s, m, 3, a);
+
 	/* Send final confirmation */
-	astman_append(s,
-	"Event: DevicelistComplete\r\n"
-	"EventList: Complete\r\n"
-	"ListItems: %d\r\n"
-	"%s"
-	"\r\n", total, idtext);
+	astman_send_list_complete_start(s, m, "DevicelistComplete", total);
+	astman_send_list_complete_end(s);
 	return 0;
 }
 
@@ -4180,7 +4182,7 @@ static char *_skinny_show_device(int type, int fd, struct mansession *s, const s
 	struct skinny_speeddial *sd;
 	struct skinny_addon *sa;
 	struct skinny_serviceurl *surl;
-	struct ast_str *codec_buf = ast_str_alloca(64);
+	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 	if (argc < 4) {
 		return CLI_SHOWUSAGE;
@@ -4384,24 +4386,17 @@ static char *_skinny_show_lines(int fd, int *total, struct mansession *s, const
 /*    Inspired from chan_sip */
 static int manager_skinny_show_lines(struct mansession *s, const struct message *m)
 {
-	const char *id = astman_get_header(m, "ActionID");
 	const char *a[] = {"skinny", "show", "lines"};
-	char idtext[256] = "";
 	int total = 0;
 
-	if (!ast_strlen_zero(id))
-		snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
-
 	astman_send_listack(s, m, "Line status list will follow", "start");
+
 	/* List the lines in separate manager events */
 	_skinny_show_lines(-1, &total, s, m, 3, a);
+
 	/* Send final confirmation */
-	astman_append(s,
-	"Event: LinelistComplete\r\n"
-	"EventList: Complete\r\n"
-	"ListItems: %d\r\n"
-	"%s"
-	"\r\n", total, idtext);
+	astman_send_list_complete_start(s, m, "LinelistComplete", total);
+	astman_send_list_complete_end(s);
 	return 0;
 }
 
@@ -4436,7 +4431,7 @@ static char *_skinny_show_line(int type, int fd, struct mansession *s, const str
 	struct skinny_device *d;
 	struct skinny_line *l;
 	struct skinny_subline *subline;
-	struct ast_str *codec_buf = ast_str_alloca(64);
+	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 	char group_buf[256];
 	char cbuf[256];
 
@@ -4874,7 +4869,7 @@ static int skinny_dialer_cb(const void *data)
 {
 	struct skinny_subchannel *sub = (struct skinny_subchannel *)data;
 	SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %u - Dialer called from SCHED %d\n", sub->callid, sub->dialer_sched);
-	sub->dialer_sched = 0;
+	sub->dialer_sched = -1;
 	skinny_dialer(sub, 1);
 	return 0;
 }
@@ -4883,7 +4878,7 @@ static int skinny_autoanswer_cb(const void *data)
 {
 	struct skinny_subchannel *sub = (struct skinny_subchannel *)data;
 	skinny_locksub(sub);
-	sub->aa_sched = 0;
+	sub->aa_sched = -1;
 	setsubstate(sub, SKINNY_CONNECTED);
 	skinny_unlocksub(sub);
 	return 0;
@@ -4893,7 +4888,7 @@ static int skinny_cfwd_cb(const void *data)
 {
 	struct skinny_subchannel *sub = (struct skinny_subchannel *)data;
 	struct skinny_line *l = sub->line;
-	sub->cfwd_sched = 0;
+	sub->cfwd_sched = -1;
 	SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %u - CFWDNOANS to %s.\n", sub->callid, l->call_forward_noanswer);
 	ast_channel_call_forward_set(sub->owner, l->call_forward_noanswer);
 	ast_queue_control(sub->owner, AST_CONTROL_REDIRECTING);
@@ -4935,7 +4930,7 @@ static int skinny_call(struct ast_channel *ast, const char *dest, int timeout)
 	skinny_locksub(sub);
 	AST_LIST_TRAVERSE(ast_channel_varshead(ast), current, entries) {
 		if (!(strcmp(ast_var_name(current), "SKINNY_AUTOANSWER"))) {
-			if (d->hookstate == SKINNY_ONHOOK && !sub->aa_sched) {
+			if (d->hookstate == SKINNY_ONHOOK && sub->aa_sched < 0) {
 				char buf[24];
 				int aatime;
 				char *stringp = buf, *curstr;
@@ -5086,7 +5081,7 @@ static int skinny_write(struct ast_channel *ast, struct ast_frame *frame)
 		}
 	} else {
 		if (ast_format_cap_iscompatible_format(ast_channel_nativeformats(ast), frame->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
-			struct ast_str *codec_buf = ast_str_alloca(64);
+			struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 			ast_log(LOG_WARNING, "Asked to transmit frame type %s, while native formats is %s (read/write = %s/%s)\n",
 				ast_format_get_name(frame->subclass.format),
 				ast_format_cap_get_names(ast_channel_nativeformats(ast), &codec_buf),
@@ -5362,7 +5357,7 @@ static struct ast_channel *skinny_new(struct skinny_line *l, struct skinny_subli
 	struct ast_format *tmpfmt;
 	struct ast_format_cap *caps;
 #ifdef AST_DEVMODE
-	struct ast_str *codec_buf = ast_str_alloca(64);
+	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 #endif
 
 	if (!l->device || !l->device->session) {
@@ -5400,9 +5395,9 @@ static struct ast_channel *skinny_new(struct skinny_line *l, struct skinny_subli
 			sub->xferor = 0;
 			sub->related = NULL;
 			sub->calldirection = direction;
-			sub->aa_sched = 0;
-			sub->dialer_sched = 0;
-			sub->cfwd_sched = 0;
+			sub->aa_sched = -1;
+			sub->dialer_sched = -1;
+			sub->cfwd_sched = -1;
 			sub->dialType = DIALTYPE_NORMAL;
 			sub->getforward = 0;
 
@@ -5559,19 +5554,19 @@ static void setsubstate(struct skinny_subchannel *sub, int state)
 
 	skinny_locksub(sub);
 
-	if (sub->dialer_sched) {
+	if (-1 < sub->dialer_sched) {
 		skinny_sched_del(sub->dialer_sched, sub);
-		sub->dialer_sched = 0;
+		sub->dialer_sched = -1;
 	}
 
-	if (state != SUBSTATE_RINGIN && sub->aa_sched) {
+	if (state != SUBSTATE_RINGIN && -1 < sub->aa_sched) {
 		skinny_sched_del(sub->aa_sched, sub);
-		sub->aa_sched = 0;
+		sub->aa_sched = -1;
 		sub->aa_beep = 0;
 		sub->aa_mute = 0;
 	}
 
-	if (sub->cfwd_sched) {
+	if (sub->cfwd_sched > -1) {
 		if (state == SUBSTATE_CONNECTED) {
 			if (skinny_sched_del(sub->cfwd_sched, sub)) {
 				SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %u - trying to change state from %s to %s, but already forwarded because no answer.\n",
@@ -5579,9 +5574,10 @@ static void setsubstate(struct skinny_subchannel *sub, int state)
 				skinny_unlocksub(sub);
 				return;
 			}
-			sub->cfwd_sched = 0;
+			sub->cfwd_sched = -1;
 		} else if (state == SUBSTATE_ONHOOK) {
 			skinny_sched_del(sub->cfwd_sched, sub);
+			sub->cfwd_sched = -1;
 		}
 	}
 
@@ -6181,9 +6177,7 @@ static int handle_ip_port_message(struct skinny_req *req, struct skinnysession *
 
 static void handle_keepalive_message(struct skinny_req *req, struct skinnysession *s)
 {
-	if (ast_sched_del(sched, s->keepalive_timeout_sched)) {
-		return;
-	}
+	not_used = ast_sched_del(sched, s->keepalive_timeout_sched);
 
 #ifdef AST_DEVMODE
 	{
@@ -6249,9 +6243,9 @@ static int handle_keypad_button_message(struct skinny_req *req, struct skinnyses
 	}
 
 	if ((sub->owner && ast_channel_state(sub->owner) <  AST_STATE_UP)) {
-		if (sub->dialer_sched && !skinny_sched_del(sub->dialer_sched, sub)) {
+		if (-1 < sub->dialer_sched && !skinny_sched_del(sub->dialer_sched, sub)) {
 			SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %u - Got a digit and not timed out, so try dialing\n", sub->callid);
-			sub->dialer_sched = 0;
+			sub->dialer_sched = -1;
 			len = strlen(sub->exten);
 			if (len == 0) {
 				transmit_stop_tone(d, l->instance, sub->callid);
@@ -6649,7 +6643,7 @@ static int handle_capabilities_res_message(struct skinny_req *req, struct skinny
 	struct ast_format_cap *codecs = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
 	int i;
 #ifdef AST_DEVMODE
-	struct ast_str *codec_buf = ast_str_alloca(64);
+	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 #endif
 	
 
@@ -7086,9 +7080,9 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse
 	case SOFTKEY_BKSPC:
 		SKINNY_DEBUG(DEBUG_PACKET, 3, "Received SOFTKEY_BKSPC from %s, inst %d, callref %d\n",
 			d->name, instance, callreference);
-		if (sub->dialer_sched && !skinny_sched_del(sub->dialer_sched, sub)) {
+		if (-1 < sub->dialer_sched && !skinny_sched_del(sub->dialer_sched, sub)) {
 			size_t len;
-			sub->dialer_sched = 0;
+			sub->dialer_sched = -1;
 			len = strlen(sub->exten);
 			if (len > 0) {
 				sub->exten[len-1] = '\0';
@@ -7426,7 +7420,7 @@ static int skinny_noauth_cb(const void *data)
 {
 	struct skinnysession *s = (struct skinnysession *)data;
 	ast_log(LOG_WARNING, "Skinny Client failed to authenticate in %d seconds (SCHED %d)\n", auth_timeout, s->auth_timeout_sched);
-	s->auth_timeout_sched = 0;
+	s->auth_timeout_sched = -1;
 	end_session(s);
 	return 0;
 }
@@ -7435,7 +7429,7 @@ static int skinny_nokeepalive_cb(const void *data)
 {
 	struct skinnysession *s = (struct skinnysession *)data;
 	ast_log(LOG_WARNING, "Skinny Client failed to send keepalive in last %d seconds (SCHED %d)\n", keep_alive*3, s->keepalive_timeout_sched);
-	s->keepalive_timeout_sched = 0;
+	s->keepalive_timeout_sched = -1;
 	end_session(s);
 	return 0;
 }
@@ -7453,11 +7447,13 @@ static void skinny_session_cleanup(void *data)
 		ast_mutex_unlock(&s->lock);
 	}
 
-	if (s->auth_timeout_sched && !ast_sched_del(sched, s->auth_timeout_sched)) {
-		s->auth_timeout_sched = 0;
+	if (-1 < s->auth_timeout_sched) {
+		not_used = ast_sched_del(sched, s->auth_timeout_sched);
+		s->auth_timeout_sched = -1;
 	}
-	if (s->keepalive_timeout_sched && !ast_sched_del(sched, s->keepalive_timeout_sched)) {
-		s->keepalive_timeout_sched = 0;
+	if (-1 < s->keepalive_timeout_sched) {
+		not_used = ast_sched_del(sched, s->keepalive_timeout_sched);
+		s->keepalive_timeout_sched = -1;
 	}
 
 	if (d) {
@@ -7498,6 +7494,7 @@ static void *skinny_session(void *data)
 	struct skinnysession *s = data;
 
 	int dlen = 0;
+	int eventmessage = 0;
 	struct pollfd fds[1];
 
 	if (!s) {
@@ -7554,7 +7551,8 @@ static void *skinny_session(void *data)
 				break;
 			}
 
-			if (letohl(req->e) < 0) {
+			eventmessage = letohl(req->e);
+			if (eventmessage < 0) {
 				ast_log(LOG_ERROR, "Event Message is NULL from socket %d, This is bad\n", s->fd);
 				break;
 			}
@@ -7660,6 +7658,8 @@ static void *accept_thread(void *ignore)
 		ast_mutex_init(&s->lock);
 		memcpy(&s->sin, &sin, sizeof(sin));
 		s->fd = as;
+		s->auth_timeout_sched = -1;
+		s->keepalive_timeout_sched = -1;
 
 		if (ast_pthread_create(&s->t, NULL, skinny_session, s)) {
 			destroy_session(s);
@@ -7690,7 +7690,7 @@ static struct ast_channel *skinny_request(const char *type, struct ast_format_ca
 	char tmp[256];
 
 	if (!(ast_format_cap_has_type(cap, AST_MEDIA_TYPE_AUDIO))) {
-		struct ast_str *codec_buf = ast_str_alloca(64);
+		struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 		ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%s'\n", ast_format_cap_get_names(cap, &codec_buf));
 		return NULL;
 	}
@@ -8718,7 +8718,6 @@ static int unload_module(void)
 	struct skinny_device *d;
 	struct skinny_line *l;
 	struct skinny_subchannel *sub;
-	struct ast_context *con;
 	pthread_t tempthread;
 
 	ast_rtp_glue_unregister(&skinny_rtp_glue);
@@ -8757,7 +8756,7 @@ static int unload_module(void)
 				skinny_unlocksub(sub);
 			}
 			if (l->mwi_event_sub) {
-				l->mwi_event_sub = stasis_unsubscribe(l->mwi_event_sub);
+				l->mwi_event_sub = stasis_unsubscribe_and_join(l->mwi_event_sub);
 			}
 			ast_mutex_unlock(&l->lock);
 			unregister_exten(l);
@@ -8779,9 +8778,7 @@ static int unload_module(void)
 		ast_sched_context_destroy(sched);
 	}
 
-	con = ast_context_find(used_context);
-	if (con)
-		ast_context_destroy(con, "Skinny");
+	ast_context_destroy_by_name(used_context, "Skinny");
 
 	ao2_ref(default_cap, -1);
 	return 0;
diff --git a/channels/chan_unistim.c b/channels/chan_unistim.c
index 80ba6f9..db4720d 100644
--- a/channels/chan_unistim.c
+++ b/channels/chan_unistim.c
@@ -38,7 +38,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 426668 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/stat.h>
 #include <signal.h>
@@ -468,7 +468,7 @@ static struct unistimsession {
 	int state;				      /*!< state of the phone (see phone_state) */
 	int size_buff_entry;	    /*!< size of the buffer used to enter datas */
 	char buff_entry[16];	    /*!< Buffer for temporary datas */
-	char macaddr[18];		       /*!< mac adress of the phone (not always available) */
+	char macaddr[18];		       /*!< mac address of the phone (not always available) */
 	char firmware[8];		       /*!< firmware of the phone (not always available) */
 	struct wsabuf wsabufsend[MAX_BUF_NUMBER];      /*!< Size of each paquet stored in the buffer array & pointer to this buffer */
 	unsigned char buf[MAX_BUF_NUMBER][MAX_BUF_SIZE];	/*!< Buffer array used to keep the lastest non-acked paquets */
@@ -935,7 +935,7 @@ static void send_raw_client(int size, const unsigned char *data, struct sockaddr
 					ast_inet_ntoa(addr_ourip->sin_addr), (int) size,
 					ast_inet_ntoa(addr_to->sin_addr));
 		for (tmp = 0; tmp < size; tmp++)
-			ast_verb(0, "%.2x ", (unsigned char) data[tmp]);
+			ast_verb(0, "%02hhx ", data[tmp]);
 		ast_verb(0, "\n******************************************\n");
 
 	}
@@ -974,7 +974,7 @@ static void send_client(int size, const unsigned char *data, struct unistimsessi
 
 /*#ifdef DUMP_PACKET */
 	if (unistimdebug) {
-		ast_verb(0, "Sending datas with seq #0x%.4x Using slot #%d :\n", (unsigned)pte->seq_server, buf_pos);
+		ast_verb(0, "Sending datas with seq #0x%04x Using slot #%d :\n", (unsigned)pte->seq_server, buf_pos);
 	}
 /*#endif */
 	send_raw_client(pte->wsabufsend[buf_pos].len, pte->wsabufsend[buf_pos].buf, &(pte->sin),
@@ -1130,7 +1130,7 @@ static void send_icon(unsigned char pos, unsigned char status, struct unistimses
 {
 	BUFFSEND;
 	if (unistimdebug) {
-		ast_verb(0, "Sending icon pos %d with status 0x%.2x\n", pos, (unsigned)status);
+		ast_verb(0, "Sending icon pos %d with status 0x%02hhx\n", pos, status);
 	}
 	memcpy(buffsend + SIZE_HEADER, packet_send_icon, sizeof(packet_send_icon));
 	buffsend[9] = pos;
@@ -1150,7 +1150,7 @@ static void send_expansion_icon(unsigned char pos, unsigned char status, struct
 {
 	BUFFSEND;
 	if (unistimdebug) {
-		ast_verb(0, "Sending expansion icon pos %d with status 0x%.2x\n", pos, (unsigned)status);
+		ast_verb(0, "Sending expansion icon pos %d with status 0x%02hhx\n", pos, status);
 	}
 	memcpy(buffsend + SIZE_HEADER, packet_send_expansion_icon, sizeof(packet_send_expansion_icon));
 	buffsend[10] = pos;
@@ -1262,7 +1262,7 @@ send_favorite(unsigned char pos, unsigned char status, struct unistimsession *pt
 	int i;
 
 	if (unistimdebug) {
-		ast_verb(0, "Sending favorite pos %d with status 0x%.2x\n", pos, (unsigned)status);
+		ast_verb(0, "Sending favorite pos %d with status 0x%02hhx\n", pos, status);
 	}
 	memcpy(buffsend + SIZE_HEADER, packet_send_favorite, sizeof(packet_send_favorite));
 	buffsend[10] = pos;
@@ -1539,7 +1539,7 @@ static int send_retransmit(struct unistimsession *pte)
 		 i < pte->last_buf_available; i++) {
 		if (i < 0) {
 			ast_log(LOG_WARNING,
-					"Asked to retransmit an ACKed slot ! last_buf_available=%d, seq_server = #0x%.4x last_seq_ack = #0x%.4x\n",
+					"Asked to retransmit an ACKed slot ! last_buf_available=%d, seq_server = #0x%04x last_seq_ack = #0x%04x\n",
 					pte->last_buf_available, (unsigned)pte->seq_server, (unsigned)pte->last_seq_ack);
 			continue;
 		}
@@ -1549,7 +1549,7 @@ static int send_retransmit(struct unistimsession *pte)
 			unsigned short seq;
 
 			seq = ntohs(sbuf[1]);
-			ast_verb(0, "Retransmit slot #%d (seq=#0x%.4x), last ack was #0x%.4x\n", i,
+			ast_verb(0, "Retransmit slot #%d (seq=#0x%04x), last ack was #0x%04x\n", i,
 						(unsigned)seq, (unsigned)pte->last_seq_ack);
 		}
 		send_raw_client(pte->wsabufsend[i].len, pte->wsabufsend[i].buf, &pte->sin,
@@ -2062,7 +2062,7 @@ static void rcv_mac_addr(struct unistimsession *pte, const unsigned char *buf)
 	char addrmac[19];
 	int res = 0;
 	for (tmp = 15; tmp < 15 + SIZE_HEADER; tmp++) {
-		sprintf(&addrmac[i], "%.2x", (unsigned) buf[tmp]);
+		sprintf(&addrmac[i], "%02hhx", buf[tmp]);
 		i += 2;
 	}
 	if (unistimdebug) {
@@ -2889,7 +2889,7 @@ static void start_rtp(struct unistim_subchannel *sub)
 	ast_rtp_instance_set_remote_address(sub->rtp, &sin_tmp);
 	if (ast_format_cap_iscompatible_format(ast_channel_nativeformats(sub->owner), ast_channel_readformat(sub->owner)) == AST_FORMAT_CMP_NOT_EQUAL) {
 		struct ast_format *tmpfmt;
-		struct ast_str *cap_buf = ast_str_alloca(64);
+		struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 		tmpfmt = ast_format_cap_get_format(ast_channel_nativeformats(sub->owner), 0);
 		ast_log(LOG_WARNING,
@@ -4484,7 +4484,7 @@ static void process_request(int size, unsigned char *buf, struct unistimsession
 		char keycode = buf[13];
 		
 		if (unistimdebug) {
-			ast_verb(0, "Expansion key pressed: keycode = 0x%.2x - current state: %s\n", (unsigned)keycode,
+			ast_verb(0, "Expansion key pressed: keycode = 0x%02hhx - current state: %s\n", (unsigned char)keycode,
 						ptestate_tostr(pte->state));
 		}
 	}
@@ -4492,7 +4492,7 @@ static void process_request(int size, unsigned char *buf, struct unistimsession
 		char keycode = buf[13];
 
 		if (unistimdebug) {
-			ast_verb(0, "Key pressed: keycode = 0x%.2x - current state: %s\n", (unsigned)keycode,
+			ast_verb(0, "Key pressed: keycode = 0x%02hhx - current state: %s\n", (unsigned char)keycode,
 						ptestate_tostr(pte->state));
 		}
 		if (keycode == KEY_MUTE) {
@@ -4660,15 +4660,14 @@ static void parsing(int size, unsigned char *buf, struct unistimsession *pte,
 		return;
 	}
 	if (buf[5] != 2) {
-		ast_log(LOG_NOTICE, "%s Wrong direction : got 0x%.2x expected 0x02\n", tmpbuf,
-				(unsigned)buf[5]);
+		ast_log(LOG_NOTICE, "%s Wrong direction : got 0x%02hhx expected 0x02\n", tmpbuf, buf[5]);
 		return;
 	}
 	seq = ntohs(sbuf[1]);
 	if (buf[4] == 1) {
 		ast_mutex_lock(&pte->lock);
 		if (unistimdebug) {
-			ast_verb(0, "ACK received for packet #0x%.4x\n", (unsigned)seq);
+			ast_verb(0, "ACK received for packet #0x%04x\n", (unsigned)seq);
 		}
 		pte->nb_retransmit = 0;
 
@@ -4684,7 +4683,7 @@ static void parsing(int size, unsigned char *buf, struct unistimsession *pte,
 				pte->last_seq_ack = 0;
 			} else {
 				ast_log(LOG_NOTICE,
-						"%s Warning : ACK received for an already ACKed packet : #0x%.4x we are at #0x%.4x\n",
+						"%s Warning : ACK received for an already ACKed packet : #0x%04x we are at #0x%04x\n",
 						tmpbuf, (unsigned)seq, (unsigned)pte->last_seq_ack);
 			}
 			ast_mutex_unlock(&pte->lock);
@@ -4692,13 +4691,13 @@ static void parsing(int size, unsigned char *buf, struct unistimsession *pte,
 		}
 		if (pte->seq_server < seq) {
 			ast_log(LOG_NOTICE,
-					"%s Error : ACK received for a non-existent packet : #0x%.4x\n",
+					"%s Error : ACK received for a non-existent packet : #0x%04x\n",
 					tmpbuf, (unsigned)pte->seq_server);
 			ast_mutex_unlock(&pte->lock);
 			return;
 		}
 		if (unistimdebug) {
-			ast_verb(0, "%s ACK gap : Received ACK #0x%.4x, previous was #0x%.4x\n",
+			ast_verb(0, "%s ACK gap : Received ACK #0x%04x, previous was #0x%04x\n",
 						tmpbuf, (unsigned)seq, (unsigned)pte->last_seq_ack);
 		}
 		pte->last_seq_ack = seq;
@@ -4722,7 +4721,7 @@ static void parsing(int size, unsigned char *buf, struct unistimsession *pte,
 		}
 		if (pte->seq_phone > seq) {
 			ast_log(LOG_NOTICE,
-					"%s Warning : received a retransmitted packet : #0x%.4x (we are at #0x%.4x)\n",
+					"%s Warning : received a retransmitted packet : #0x%04x (we are at #0x%04x)\n",
 					tmpbuf, (unsigned)seq, (unsigned)pte->seq_phone);
 			/* BUG ? pte->device->seq_phone = seq; */
 			/* Send ACK */
@@ -4732,29 +4731,28 @@ static void parsing(int size, unsigned char *buf, struct unistimsession *pte,
 			return;
 		}
 		ast_log(LOG_NOTICE,
-				"%s Warning : we lost a packet : received #0x%.4x (we are at #0x%.4x)\n",
+				"%s Warning : we lost a packet : received #0x%04x (we are at #0x%04x)\n",
 				tmpbuf, (unsigned)seq, (unsigned)pte->seq_phone);
 		return;
 	}
 	if (buf[4] == 0) {
-		ast_log(LOG_NOTICE, "%s Retransmit request for packet #0x%.4x\n", tmpbuf, (unsigned)seq);
+		ast_log(LOG_NOTICE, "%s Retransmit request for packet #0x%04x\n", tmpbuf, (unsigned)seq);
 		if (pte->last_seq_ack > seq) {
 			ast_log(LOG_NOTICE,
-					"%s Error : received a request for an already ACKed packet : #0x%.4x\n",
+					"%s Error : received a request for an already ACKed packet : #0x%04x\n",
 					tmpbuf, (unsigned)pte->last_seq_ack);
 			return;
 		}
 		if (pte->seq_server < seq) {
 			ast_log(LOG_NOTICE,
-					"%s Error : received a request for a non-existent packet : #0x%.4x\n",
+					"%s Error : received a request for a non-existent packet : #0x%04x\n",
 					tmpbuf, (unsigned)pte->seq_server);
 			return;
 		}
 		send_retransmit(pte);
 		return;
 	}
-	ast_log(LOG_NOTICE, "%s Unknown request : got 0x%.2x expected 0x00,0x01 or 0x02\n",
-			tmpbuf, (unsigned)buf[4]);
+	ast_log(LOG_NOTICE, "%s Unknown request : got 0x%02hhx expected 0x00,0x01 or 0x02\n", tmpbuf, buf[4]);
 	return;
 }
 
@@ -5096,7 +5094,7 @@ static int unistimsock_read(int *id, int fd, short events, void *ignore)
 					dw_num_bytes_rcvd, ast_inet_ntoa(addr_from.sin_addr), tmp);
 	for (dw_num_bytes_rcvdd = 0; dw_num_bytes_rcvdd < dw_num_bytes_rcvd;
 		 dw_num_bytes_rcvdd++)
-		ast_verb(0, "%.2x ", (unsigned char) buff[dw_num_bytes_rcvdd]);
+		ast_verb(0, "%02hhx ", buff[dw_num_bytes_rcvdd]);
 	ast_verb(0, "\n******************************************\n");
 #endif
 
@@ -5144,7 +5142,7 @@ static struct ast_frame *unistim_rtp_read(const struct ast_channel *ast,
 		/* We already hold the channel lock */
 		if (f->frametype == AST_FRAME_VOICE) {
 			if (ast_format_cap_iscompatible_format(ast_channel_nativeformats(sub->owner), f->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
-				struct ast_str *cap_buf = ast_str_alloca(64);
+				struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 				struct ast_format_cap *caps;
 
 				ast_debug(1,
@@ -5194,7 +5192,7 @@ static int unistim_write(struct ast_channel *ast, struct ast_frame *frame)
 		}
 	} else {
 		if (ast_format_cap_iscompatible_format(ast_channel_nativeformats(ast), frame->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
-			struct ast_str *cap_buf = ast_str_alloca(64);
+			struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 			ast_log(LOG_WARNING,
 					"Asked to transmit frame type %s, while native formats is %s (read/write = (%s/%s)\n",
@@ -5725,9 +5723,9 @@ static struct ast_channel *unistim_new(struct unistim_subchannel *sub, int state
 	tmpfmt = ast_format_cap_get_format(ast_channel_nativeformats(tmp), 0);
 
 	if (unistimdebug) {
-		struct ast_str *native_buf = ast_str_alloca(64);
-		struct ast_str *cap_buf = ast_str_alloca(64);
-		struct ast_str *global_buf = ast_str_alloca(64);
+		struct ast_str *native_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+		struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+		struct ast_str *global_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 		ast_verb(0, "Best codec = %s from nativeformats %s (line cap=%s global=%s)\n",
 			ast_format_get_name(tmpfmt),
@@ -5940,8 +5938,8 @@ static struct ast_channel *unistim_request(const char *type, struct ast_format_c
 	char tmp[256];
 
 	if (!(ast_format_cap_iscompatible(cap, global_cap))) {
-		struct ast_str *cap_buf = ast_str_alloca(64);
-		struct ast_str *global_buf = ast_str_alloca(64);
+		struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+		struct ast_str *global_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 		ast_log(LOG_NOTICE,
 				"Asked to get a channel of unsupported format %s while capability is %s\n",
 				ast_format_cap_get_names(cap, &cap_buf),
@@ -6016,7 +6014,7 @@ static char *unistim_show_info(struct ast_cli_entry *e, int cmd, struct ast_cli_
 	struct unistim_line *line;
 	struct unistim_subchannel *sub;
 	struct unistimsession *s;
-	struct ast_str *cap_buf = ast_str_alloca(64);
+	struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 	switch (cmd) {
 	case CLI_INIT:
diff --git a/channels/chan_vpb.cc b/channels/chan_vpb.cc
index a04f89d..0f050a8 100644
--- a/channels/chan_vpb.cc
+++ b/channels/chan_vpb.cc
@@ -59,7 +59,7 @@ extern "C" {
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419044 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/lock.h"
 #include "asterisk/utils.h"
@@ -2508,7 +2508,7 @@ static struct ast_channel *vpb_request(const char *type, struct ast_format_cap *
 	if (!(ast_format_cap_iscompatible_format(cap, ast_format_slin))) {
 		struct ast_str *buf;
 
-		buf = ast_str_create(256);
+		buf = ast_str_create(AST_FORMAT_CAP_NAMES_LEN);
 		if (!buf) {
 			return NULL;
 		}
@@ -2626,14 +2626,13 @@ static int unload_module(void)
 
 	if (bridges) {
 		ast_mutex_lock(&bridge_lock);
-		memset(bridges, 0, sizeof bridges);
-		ast_mutex_unlock(&bridge_lock);
-		ast_mutex_destroy(&bridge_lock);
 		for (int i = 0; i < max_bridges; i++) {
 			ast_mutex_destroy(&bridges[i].lock);
 			ast_cond_destroy(&bridges[i].cond);
 		}
 		ast_free(bridges);
+		bridges = NULL;
+		ast_mutex_unlock(&bridge_lock);
 	}
 
 	ao2_cleanup(vpb_tech.capabilities);
diff --git a/channels/console_board.c b/channels/console_board.c
index b988bb6..6c920a9 100644
--- a/channels/console_board.c
+++ b/channels/console_board.c
@@ -13,7 +13,7 @@
  * the GNU General Public License Version 2. See the LICENSE file
  * at the top of the source tree.
  *
- * $Revision: 369013 $
+ * $Revision$
  */
 
 /* 
@@ -42,7 +42,7 @@
  ***/
 
 #include "asterisk.h"	/* ast_strdupa */
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 369013 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/utils.h"	/* ast_strdupa */
 #include "console_video.h"	/* ast_strdupa */
 
diff --git a/channels/console_gui.c b/channels/console_gui.c
index 0b9d393..916c408 100644
--- a/channels/console_gui.c
+++ b/channels/console_gui.c
@@ -1,7 +1,7 @@
 /*
  * GUI for console video.
  * The routines here are in charge of loading the keypad and handling events.
- * $Revision: 369013 $
+ * $Revision$
  */
 
 /*
diff --git a/channels/console_video.c b/channels/console_video.c
index 5d2f20d..a1db5cd 100644
--- a/channels/console_video.c
+++ b/channels/console_video.c
@@ -30,7 +30,7 @@
  * thus not compiling in AST_DEVMODE, or don't have swscale, in which case
  * you can try to compile #defining OLD_FFMPEG here.
  *
- * $Revision: 369013 $
+ * $Revision$
  */
 
 //#define DROP_PACKETS 5       /* if set, drop this % of video packets */
@@ -41,7 +41,7 @@
  ***/
 
 #include "asterisk.h"
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 369013 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include <sys/ioctl.h>
 #include "asterisk/cli.h"
 #include "asterisk/file.h"
diff --git a/channels/console_video.h b/channels/console_video.h
index dee0147..f88e5fa 100644
--- a/channels/console_video.h
+++ b/channels/console_video.h
@@ -17,7 +17,7 @@
 /*
  * Common header for console video support
  *
- * $Revision: 126572 $
+ * $Revision$
  */
 
 #ifndef CONSOLE_VIDEO_H
diff --git a/channels/dahdi/bridge_native_dahdi.c b/channels/dahdi/bridge_native_dahdi.c
index b2cd2e0..9df82b2 100644
--- a/channels/dahdi/bridge_native_dahdi.c
+++ b/channels/dahdi/bridge_native_dahdi.c
@@ -32,7 +32,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419044 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "../sig_analog.h"
 #if defined(HAVE_PRI)
@@ -893,7 +893,6 @@ static struct ast_bridge_technology native_bridge = {
 void dahdi_native_unload(void)
 {
 	ast_bridge_technology_unregister(&native_bridge);
-	ao2_cleanup(native_bridge.format_capabilities);
 }
 
 /*!
@@ -908,18 +907,10 @@ int dahdi_native_load(struct ast_module *mod, const struct ast_channel_tech *tec
 {
 	dahdi_tech = tech;
 
-	native_bridge.format_capabilities = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
-	if (!native_bridge.format_capabilities) {
+	if (__ast_bridge_technology_register(&native_bridge, mod)) {
+		dahdi_native_unload();
 		return -1;
 	}
 
-	/*
-	 * This is used to make channels compatible with the bridge
-	 * itself not with each other.
-	 */
-	ast_format_cap_append(native_bridge.format_capabilities, ast_format_slin, 0);
-	ast_format_cap_append(native_bridge.format_capabilities, ast_format_ulaw, 0);
-	ast_format_cap_append(native_bridge.format_capabilities, ast_format_alaw, 0);
-
-	return __ast_bridge_technology_register(&native_bridge, mod);
+	return 0;
 }
diff --git a/channels/iax2/codec_pref.c b/channels/iax2/codec_pref.c
index 814f850..85167c3 100644
--- a/channels/iax2/codec_pref.c
+++ b/channels/iax2/codec_pref.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420364 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/logger.h"
 #include "asterisk/astobj2.h"
diff --git a/channels/iax2/firmware.c b/channels/iax2/firmware.c
index ea240fb..a1ee435 100644
--- a/channels/iax2/firmware.c
+++ b/channels/iax2/firmware.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 413589 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/types.h>
 #include <sys/stat.h>
diff --git a/channels/iax2/format_compatibility.c b/channels/iax2/format_compatibility.c
index ee61516..be7852c 100644
--- a/channels/iax2/format_compatibility.c
+++ b/channels/iax2/format_compatibility.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420364 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/logger.h"
 #include "asterisk/astobj2.h"
diff --git a/channels/iax2/parser.c b/channels/iax2/parser.c
index 57f245b..2a3eabf 100644
--- a/channels/iax2/parser.c
+++ b/channels/iax2/parser.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419044 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/socket.h>
 #include <netinet/in.h>
@@ -111,7 +111,7 @@ static void dump_string_hex(char *output, int maxlen, void *value, int len)
 	int i = 0;
 
 	while (len-- && (i + 1) * 4 < maxlen) {
-		sprintf(output + (4 * i), "\\x%2.2x", (unsigned)*((unsigned char *)value + i));
+		sprintf(output + (4 * i), "\\x%02hhx", *((unsigned char *)value + i));
 		i++;
 	}
 }
diff --git a/channels/iax2/provision.c b/channels/iax2/provision.c
index 968cf0e..85dfe94 100644
--- a/channels/iax2/provision.c
+++ b/channels/iax2/provision.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419044 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <netdb.h>
 #include <netinet/in.h>
diff --git a/channels/misdn/Makefile b/channels/misdn/Makefile
index 194bef5..96d5a2a 100644
--- a/channels/misdn/Makefile
+++ b/channels/misdn/Makefile
@@ -14,4 +14,4 @@ portinfo: portinfo.o
 	$(CC) -o $@ $^ -lisdnnet -lmISDN -lpthread
 
 clean:
-	rm -rf *.a *.o *.so portinfo *.i
+	rm -rf *.a *.o *.so portinfo *.i *.gcda *.gcno
diff --git a/channels/misdn/ie.c b/channels/misdn/ie.c
index 74ae897..df5df9a 100644
--- a/channels/misdn/ie.c
+++ b/channels/misdn/ie.c
@@ -293,7 +293,7 @@ static void enc_ie_call_id(unsigned char **ntmode, msg_t *msg, char *callid, int
 	i = 0;
 	while(i < callid_len)
 	{
-		if (MISDN_IE_DEBG) printf(debug+(i*3), " %02x", callid[i]);
+		if (MISDN_IE_DEBG) printf(debug+(i*3), " %02hhx", (unsigned char)callid[i]);
 		i++;
 	}
 
@@ -339,7 +339,7 @@ static void dec_ie_call_id(unsigned char *p, Q931_info_t *qi, char *callid, int
 	i = 0;
 	while(i < *callid_len)
 	{
-		if (MISDN_IE_DEBG) printf(debug+(i*3), " %02x", callid[i]);
+		if (MISDN_IE_DEBG) printf(debug+(i*3), " %02hhx", (unsigned char)callid[i]);
 		i++;
 	}
 
@@ -745,7 +745,7 @@ static void enc_ie_channel_id(unsigned char **ntmode, msg_t *msg, int exclusive,
 			p[0] = IE_CHANNEL_ID;
 			p[1] = l;
 			p[2] = 0x80 + 0x20 + 0x03;
-/* 			if (MISDN_IE_DEBG) printf("%02x\n", p[2]); */
+/* 			if (MISDN_IE_DEBG) printf("%02hhx\n", p[2]); */
 			return; /* end */
 		}
 		l = 3;
@@ -759,7 +759,7 @@ static void enc_ie_channel_id(unsigned char **ntmode, msg_t *msg, int exclusive,
 		p[2] = 0x80 + 0x20 + (exclusive<<3) + 0x01;
 		p[3] = 0x80 + 3; /* CCITT, Number, B-type */
 		p[4] = 0x80 + channel;
-/* 		if (MISDN_IE_DEBG) printf("%02x %02x %02x\n", p[2], p[3], p[4]); */
+/* 		if (MISDN_IE_DEBG) printf("%02hhx %02hhx %02hhx\n", p[2], p[3], p[4]); */
 	}
 }
 
@@ -849,7 +849,7 @@ static void dec_ie_channel_id(unsigned char *p, Q931_info_t *qi, int *exclusive,
 			printf("%s: ERROR: PRI interface channel out of range (%d).\n", __FUNCTION__, *channel);
 			return;
 		}
-/* 		if (MISDN_IE_DEBG) printf("%02x %02x %02x\n", p[1], p[2], p[3]); */
+/* 		if (MISDN_IE_DEBG) printf("%02hhx %02hhx %02hhx\n", p[1], p[2], p[3]); */
 	}
 
 	if (MISDN_IE_DEBG) printf("    exclusive=%d channel=%d\n", *exclusive, *channel);
@@ -1342,7 +1342,7 @@ static void enc_ie_useruser(unsigned char **ntmode, msg_t *msg, int protocol, ch
 		char debug[768];
 
 		for (i = 0; i < user_len; ++i) {
-			sprintf(debug + (i * 3), " %02x", user[i]);
+			sprintf(debug + (i * 3), " %02hhx", (unsigned char)user[i]);
 		}
 		debug[i * 3] = 0;
 		printf("    protocol=%d user-user%s\n", protocol, debug);
@@ -1387,7 +1387,7 @@ static void dec_ie_useruser(unsigned char *p, Q931_info_t *qi, int *protocol, ch
 		char debug[768];
 
 		for (i = 0; i < *user_len; ++i) {
-			sprintf(debug + (i * 3), " %02x", user[i]);
+			sprintf(debug + (i * 3), " %02hhx", (unsigned char)user[i]);
 		}
 		debug[i * 3] = 0;
 		printf("    protocol=%d user-user%s\n", *protocol, debug);
diff --git a/channels/misdn_config.c b/channels/misdn_config.c
index d77cd34..32f92c3 100644
--- a/channels/misdn_config.c
+++ b/channels/misdn_config.c
@@ -32,7 +32,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 370831 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "chan_misdn_config.h"
 
diff --git a/channels/pjsip/dialplan_functions.c b/channels/pjsip/dialplan_functions.c
index 91129a4..eb6def6 100644
--- a/channels/pjsip/dialplan_functions.c
+++ b/channels/pjsip/dialplan_functions.c
@@ -298,6 +298,18 @@
 			Use the <replaceable>PJSIP_ENDPOINT</replaceable> function to obtain
 			further endpoint related information.</para>
 		</enum>
+		<enum name="contact">
+			<para>R/O The name of the contact associated with this channel.
+			Use the <replaceable>PJSIP_CONTACT</replaceable> function to obtain
+			further contact related information. Note this may not be present and if so
+			is only available on outgoing legs.</para>
+		</enum>
+		<enum name="aor">
+			<para>R/O The name of the AOR associated with this channel.
+			Use the <replaceable>PJSIP_AOR</replaceable> function to obtain
+			further AOR related information. Note this may not be present and if so
+			is only available on outgoing legs.</para>
+		</enum>
 		<enum name="pjsip">
 			<para>R/O Obtain information about the current PJSIP channel and its
 			session.</para>
@@ -306,6 +318,9 @@
 				<literal>type</literal> parameter must be provided. It specifies
 				which signalling parameter to read.</para>
 				<enumlist>
+					<enum name="call-id">
+						<para>The SIP call-id.</para>
+					</enum>
 					<enum name="secure">
 						<para>Whether or not the signalling uses a secure transport.</para>
 						<enumlist>
@@ -357,7 +372,7 @@
 #include <pjlib.h>
 #include <pjsip_ua.h>
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 424622 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/astobj2.h"
 #include "asterisk/module.h"
@@ -579,8 +594,22 @@ static int channel_read_pjsip(struct ast_channel *chan, const char *type, const
 
 	dlg = channel->session->inv_session->dlg;
 
-	if (!strcmp(type, "secure")) {
-		snprintf(buf, buflen, "%d", dlg->secure ? 1 : 0);
+	if (ast_strlen_zero(type)) {
+		ast_log(LOG_WARNING, "You must supply a type field for 'pjsip' information\n");
+		return -1;
+	} else if (!strcmp(type, "call-id")) {
+		snprintf(buf, buflen, "%.*s", (int) pj_strlen(&dlg->call_id->id), pj_strbuf(&dlg->call_id->id));
+	} else if (!strcmp(type, "secure")) {
+#ifdef HAVE_PJSIP_GET_DEST_INFO
+		pjsip_host_info dest;
+		pj_pool_t *pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "secure-check", 128, 128);
+		pjsip_get_dest_info(dlg->target, NULL, pool, &dest);
+		snprintf(buf, buflen, "%d", dest.flag & PJSIP_TRANSPORT_SECURE ? 1 : 0);
+		pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
+#else
+		ast_log(LOG_WARNING, "Asterisk has been built against a version of pjproject which does not have the required functionality to support the 'secure' argument. Please upgrade to version 2.3 or later.\n");
+		return -1;
+#endif
 	} else if (!strcmp(type, "target_uri")) {
 		pjsip_uri_print(PJSIP_URI_IN_REQ_URI, dlg->target, buf, buflen);
 		buf_copy = ast_strdupa(buf);
@@ -667,6 +696,28 @@ static int read_pjsip(void *data)
 			return -1;
 		}
 		snprintf(func_args->buf, func_args->len, "%s", ast_sorcery_object_get_id(pvt->session->endpoint));
+	} else if (!strcmp(func_args->param, "contact")) {
+		struct ast_sip_channel_pvt *pvt = ast_channel_tech_pvt(func_args->chan);
+
+		if (!pvt) {
+			ast_log(AST_LOG_WARNING, "Channel %s has no pvt!\n", ast_channel_name(func_args->chan));
+			return -1;
+		}
+		if (!pvt->session || !pvt->session->contact) {
+			return 0;
+		}
+		snprintf(func_args->buf, func_args->len, "%s", ast_sorcery_object_get_id(pvt->session->contact));
+	} else if (!strcmp(func_args->param, "aor")) {
+		struct ast_sip_channel_pvt *pvt = ast_channel_tech_pvt(func_args->chan);
+
+		if (!pvt) {
+			ast_log(AST_LOG_WARNING, "Channel %s has no pvt!\n", ast_channel_name(func_args->chan));
+			return -1;
+		}
+		if (!pvt->session || !pvt->session->aor) {
+			return 0;
+		}
+		snprintf(func_args->buf, func_args->len, "%s", ast_sorcery_object_get_id(pvt->session->aor));
 	} else if (!strcmp(func_args->param, "pjsip")) {
 		func_args->ret = channel_read_pjsip(func_args->chan, func_args->type,
 		                                    func_args->field, func_args->buf,
@@ -820,11 +871,11 @@ static int media_offer_read_av(struct ast_sip_session *session, char *buf,
 
 		/* add one since we'll include a comma */
 		size = strlen(ast_format_get_name(fmt)) + 1;
-		len -= size;
-		if ((len) < 0) {
+		if (len < size) {
 			ao2_ref(fmt, -1);
 			break;
 		}
+		len -= size;
 
 		/* no reason to use strncat here since we have already ensured buf has
                    enough space, so strcat can be safely used */
diff --git a/channels/sig_analog.c b/channels/sig_analog.c
index 140f509..1d44a29 100644
--- a/channels/sig_analog.c
+++ b/channels/sig_analog.c
@@ -1636,6 +1636,9 @@ static void analog_decrease_ss_count(void)
 
 static int analog_distinctive_ring(struct ast_channel *chan, struct analog_pvt *p, int idx, int *ringdata)
 {
+	if (!p->usedistinctiveringdetection) {
+		return 0;
+	}
 	if (analog_callbacks.distinctive_ring) {
 		return analog_callbacks.distinctive_ring(chan, p->chan_pvt, idx, ringdata);
 	}
@@ -1703,7 +1706,6 @@ static void *__analog_ss_thread(void *data)
 	char dtmfbuf[300];
 	char namebuf[ANALOG_MAX_CID];
 	char numbuf[ANALOG_MAX_CID];
-	struct callerid_state *cs = NULL;
 	char *name = NULL, *number = NULL;
 	int flags = 0;
 	struct ast_smdi_md_message *smdi_msg = NULL;
@@ -1963,6 +1965,8 @@ static void *__analog_ss_thread(void *data)
 		if ((p->sig == ANALOG_SIG_FEATDMF) || (p->sig == ANALOG_SIG_FEATDMF_TA)) {
 			if (exten[0] == '*') {
 				char *stringp=NULL;
+				struct ast_party_caller *caller;
+
 				ast_copy_string(exten2, exten, sizeof(exten2));
 				/* Parse out extension and callerid */
 				stringp=exten2 +1;
@@ -1980,6 +1984,11 @@ static void *__analog_ss_thread(void *data)
 				} else {
 					ast_copy_string(exten, s1 + 2, sizeof(exten));
 				}
+
+				/* The first two digits are ani2 information. */
+				caller = ast_channel_caller(chan);
+				s1[2] = '\0';
+				caller->ani2 = atoi(s1);
 			} else {
 				ast_log(LOG_WARNING, "Got a non-Feature Group D input on channel %d.  Assuming E&M Wink instead\n", p->channel);
 			}
@@ -2384,7 +2393,6 @@ static void *__analog_ss_thread(void *data)
 				int timeout_ms;
 				int ms;
 				struct timeval start = ast_tvnow();
-				cs = NULL;
 				ast_debug(1, "Receiving DTMF cid on channel %s\n", ast_channel_name(chan));
 
 				oldlinearity = analog_set_linear_mode(p, idx, 0);
@@ -2408,8 +2416,8 @@ static void *__analog_ss_thread(void *data)
 						 * or AST_FLAG_END_DTMF_ONLY flag settings since we
 						 * are hanging up the channel.
 						 */
-						ast_log(LOG_WARNING, "DTMFCID timed out waiting for ring. "
-							"Exiting simple switch\n");
+						ast_log(LOG_WARNING,
+							"DTMFCID timed out waiting for ring. Exiting simple switch\n");
 						ast_hangup(chan);
 						goto quit;
 					}
@@ -2448,50 +2456,53 @@ static void *__analog_ss_thread(void *data)
 
 			/* If set to use V23 Signalling, launch our FSK gubbins and listen for it */
 			} else if ((p->cid_signalling == CID_SIG_V23) || (p->cid_signalling == CID_SIG_V23_JP)) {
-				int timeout = 10000;  /* Ten seconds */
-				struct timeval start = ast_tvnow();
-				enum analog_event ev;
-
 				namebuf[0] = 0;
 				numbuf[0] = 0;
 
 				if (!analog_start_cid_detect(p, p->cid_signalling)) {
+					int timeout = 10000;  /* Ten seconds */
+					struct timeval start = ast_tvnow();
+					enum analog_event ev;
 					int off_ms;
 					int ms;
 					struct timeval off_start;
-					while (1) {
-						res = analog_get_callerid(p, namebuf, numbuf, &ev, timeout - ast_tvdiff_ms(ast_tvnow(), start));
 
+					if (!p->usedistinctiveringdetection) {
+						/* Disable distinctive ring timeout count */
+						analog_set_ringtimeout(p, 0);
+					}
+					while ((ms = ast_remaining_ms(start, timeout))) {
+						res = analog_get_callerid(p, namebuf, numbuf, &ev, ms);
+						if (res < 0) {
+							ast_log(LOG_WARNING,
+								"CallerID returned with error on channel '%s'\n",
+								ast_channel_name(chan));
+							break;
+						}
 						if (res == 0) {
 							break;
 						}
-
-						if (res == 1) {
-							if (ev == ANALOG_EVENT_NOALARM) {
-								analog_set_alarm(p, 0);
-							}
-							if (p->cid_signalling == CID_SIG_V23_JP) {
-								if (ev == ANALOG_EVENT_RINGBEGIN) {
-									analog_off_hook(p);
-									usleep(1);
-								}
-							} else {
-								ev = ANALOG_EVENT_NONE;
-								break;
-							}
+						if (res != 1) {
+							continue;
 						}
-
-						if (ast_tvdiff_ms(ast_tvnow(), start) > timeout)
+						if (ev == ANALOG_EVENT_NOALARM) {
+							analog_set_alarm(p, 0);
+						}
+						if (p->cid_signalling == CID_SIG_V23_JP) {
+							if (ev == ANALOG_EVENT_RINGBEGIN) {
+								analog_off_hook(p);
+								usleep(1);
+							}
+						} else {
 							break;
-
+						}
 					}
+
 					name = namebuf;
 					number = numbuf;
 
-					analog_stop_cid_detect(p);
-
 					if (p->cid_signalling == CID_SIG_V23_JP) {
-						res = analog_on_hook(p);
+						analog_on_hook(p);
 						usleep(1);
 					}
 
@@ -2503,13 +2514,15 @@ static void *__analog_ss_thread(void *data)
 
 						res = ast_waitfor(chan, ms);
 						if (res <= 0) {
-							ast_log(LOG_WARNING, "CID timed out waiting for ring. "
-								"Exiting simple switch\n");
+							ast_log(LOG_WARNING,
+								"CID timed out waiting for ring. Exiting simple switch\n");
+							analog_stop_cid_detect(p);
 							ast_hangup(chan);
 							goto quit;
 						}
 						if (!(f = ast_read(chan))) {
 							ast_log(LOG_WARNING, "Hangup received waiting for ring. Exiting simple switch\n");
+							analog_stop_cid_detect(p);
 							ast_hangup(chan);
 							goto quit;
 						}
@@ -2519,91 +2532,86 @@ static void *__analog_ss_thread(void *data)
 							break; /* Got ring */
 					}
 
-					if (analog_distinctive_ring(chan, p, idx, NULL)) {
+					res = analog_distinctive_ring(chan, p, idx, NULL);
+					analog_stop_cid_detect(p);
+					if (res) {
 						goto quit;
 					}
-
-					if (res < 0) {
-						ast_log(LOG_WARNING, "CallerID returned with error on channel '%s'\n", ast_channel_name(chan));
-					}
 				} else {
 					ast_log(LOG_WARNING, "Unable to get caller ID space\n");
 				}
 			} else {
-				ast_log(LOG_WARNING, "Channel %s in prering "
-					"state, but I have nothing to do. "
-					"Terminating simple switch, should be "
-					"restarted by the actual ring.\n",
+				ast_log(LOG_WARNING,
+					"Channel %s in prering state, but I have nothing to do. Terminating simple switch, should be restarted by the actual ring.\n",
 					ast_channel_name(chan));
 				ast_hangup(chan);
 				goto quit;
 			}
 		} else if (p->use_callerid && p->cid_start == ANALOG_CID_START_RING) {
-			int timeout = 10000;  /* Ten seconds */
-			struct timeval start = ast_tvnow();
-			enum analog_event ev;
-			int curRingData[RING_PATTERNS] = { 0 };
-			int receivedRingT = 0;
-
 			namebuf[0] = 0;
 			numbuf[0] = 0;
 
 			if (!analog_start_cid_detect(p, p->cid_signalling)) {
-				while (1) {
-					res = analog_get_callerid(p, namebuf, numbuf, &ev, timeout - ast_tvdiff_ms(ast_tvnow(), start));
+				int timeout = 10000;  /* Ten seconds */
+				struct timeval start = ast_tvnow();
+				enum analog_event ev;
+				int ring_data[RING_PATTERNS] = { 0 };
+				int ring_data_idx = 0;
+				int ms;
 
+				if (!p->usedistinctiveringdetection) {
+					/* Disable distinctive ring timeout count */
+					analog_set_ringtimeout(p, 0);
+				}
+				while ((ms = ast_remaining_ms(start, timeout))) {
+					res = analog_get_callerid(p, namebuf, numbuf, &ev, ms);
+					if (res < 0) {
+						ast_log(LOG_WARNING,
+							"CallerID returned with error on channel '%s'\n",
+							ast_channel_name(chan));
+						break;
+					}
 					if (res == 0) {
 						break;
 					}
-
-					if (res == 1 || res == 2) {
-						if (ev == ANALOG_EVENT_NOALARM) {
-							analog_set_alarm(p, 0);
-						} else if (ev == ANALOG_EVENT_POLARITY && p->hanguponpolarityswitch && p->polarity == POLARITY_REV) {
-							ast_debug(1, "Hanging up due to polarity reversal on channel %d while detecting callerid\n", p->channel);
-							p->polarity = POLARITY_IDLE;
-							ast_hangup(chan);
-							goto quit;
-						} else if (ev != ANALOG_EVENT_NONE && ev != ANALOG_EVENT_RINGBEGIN && ev != ANALOG_EVENT_RINGOFFHOOK) {
-							break;
-						}
-						if (res != 2) {
-							/* Let us detect callerid when the telco uses distinctive ring */
-							curRingData[receivedRingT] = p->ringt;
-
-							if (p->ringt < p->ringt_base/2) {
-								break;
-							}
-							/* Increment the ringT counter so we can match it against
-							   values in chan_dahdi.conf for distinctive ring */
-							if (++receivedRingT == RING_PATTERNS) {
-								break;
-							}
-						}
+					if (res != 1) {
+						continue;
 					}
-
-					if (ast_tvdiff_ms(ast_tvnow(), start) > timeout) {
-						break;
+					if (ev == ANALOG_EVENT_NOALARM) {
+						analog_set_alarm(p, 0);
+					} else if (ev == ANALOG_EVENT_POLARITY
+						&& p->hanguponpolarityswitch
+						&& p->polarity == POLARITY_REV) {
+						ast_debug(1,
+							"Hanging up due to polarity reversal on channel %d while detecting callerid\n",
+							p->channel);
+						p->polarity = POLARITY_IDLE;
+						analog_stop_cid_detect(p);
+						ast_hangup(chan);
+						goto quit;
+					} else if (ev == ANALOG_EVENT_RINGOFFHOOK
+						&& p->usedistinctiveringdetection
+						&& ring_data_idx < RING_PATTERNS) {
+						/*
+						 * Detect callerid while collecting possible
+						 * distinctive ring pattern.
+						 */
+						ring_data[ring_data_idx] = p->ringt;
+						++ring_data_idx;
 					}
-
 				}
+
 				name = namebuf;
 				number = numbuf;
 
+				res = analog_distinctive_ring(chan, p, idx, ring_data);
 				analog_stop_cid_detect(p);
-
-				if (analog_distinctive_ring(chan, p, idx, curRingData)) {
+				if (res) {
 					goto quit;
 				}
-
-				if (res < 0) {
-					ast_log(LOG_WARNING, "CallerID returned with error on channel '%s'\n", ast_channel_name(chan));
-				}
 			} else {
 				ast_log(LOG_WARNING, "Unable to get caller ID space\n");
 			}
-		} else {
-			cs = NULL;
 		}
 
 		if (number) {
@@ -2611,10 +2619,6 @@ static void *__analog_ss_thread(void *data)
 		}
 		ast_set_callerid(chan, number, name, number);
 
-		if (cs) {
-			callerid_free(cs);
-		}
-
 		analog_handle_notify_message(chan, p, flags, -1);
 
 		ast_channel_lock(chan);
@@ -3839,8 +3843,7 @@ void *analog_handle_init_event(struct analog_pvt *i, int event)
 			}
 			if (i->cid_start == ANALOG_CID_START_POLARITY || i->cid_start == ANALOG_CID_START_POLARITY_IN) {
 				i->polarity = POLARITY_REV;
-				ast_verb(2, "Starting post polarity "
-					"CID detection on channel %d\n",
+				ast_verb(2, "Starting post polarity CID detection on channel %d\n",
 					i->channel);
 				chan = analog_new_ast_channel(i, AST_STATE_PRERING, 0, ANALOG_SUB_REAL, NULL);
 				i->ss_astchan = chan;
@@ -3854,9 +3857,9 @@ void *analog_handle_init_event(struct analog_pvt *i, int event)
 			ast_callid_threadstorage_auto_clean(callid, callid_created);
 			break;
 		default:
-			ast_log(LOG_WARNING, "handle_init_event detected "
-				"polarity reversal on non-FXO (ANALOG_SIG_FXS) "
-				"interface %d\n", i->channel);
+			ast_log(LOG_WARNING,
+				"handle_init_event detected polarity reversal on non-FXO (ANALOG_SIG_FXS) interface %d\n",
+				i->channel);
 			break;
 		}
 		break;
@@ -3881,9 +3884,9 @@ void *analog_handle_init_event(struct analog_pvt *i, int event)
 			ast_callid_threadstorage_auto_clean(callid, callid_created);
 			break;
 		default:
-			ast_log(LOG_WARNING, "handle_init_event detected "
-				"dtmfcid generation event on non-FXO (ANALOG_SIG_FXS) "
-				"interface %d\n", i->channel);
+			ast_log(LOG_WARNING,
+				"handle_init_event detected dtmfcid generation event on non-FXO (ANALOG_SIG_FXS) interface %d\n",
+				i->channel);
 			break;
 		}
 		break;
diff --git a/channels/sig_analog.h b/channels/sig_analog.h
index 13c92c6..6415b6e 100644
--- a/channels/sig_analog.h
+++ b/channels/sig_analog.h
@@ -281,6 +281,7 @@ struct analog_pvt {
 	unsigned int transfer:1;
 	unsigned int transfertobusy:1;			/*!< allow flash-transfers to busy channels */
 	unsigned int use_callerid:1;			/*!< Whether or not to use caller id on this channel */
+	unsigned int usedistinctiveringdetection:1;
 	unsigned int callwaitingcallerid:1;		/*!< TRUE if send caller ID for Call Waiting */
 	/*!
 	 * \brief TRUE if SMDI (Simplified Message Desk Interface) is enabled
diff --git a/channels/sig_pri.c b/channels/sig_pri.c
index a26b566..d6b8507 100644
--- a/channels/sig_pri.c
+++ b/channels/sig_pri.c
@@ -133,15 +133,6 @@
  */
 //#define ALWAYS_PICK_CHANNEL	1
 
-/*!
- * Define to force a RESTART on a channel that returns a cause
- * code of PRI_CAUSE_REQUESTED_CHAN_UNAVAIL(44).  If the cause
- * is because of a stuck channel on the peer and the channel is
- * always the next channel we pick for an outgoing call then
- * this can help.
- */
-#define FORCE_RESTART_UNAVAIL_CHANS		1
-
 #if defined(HAVE_PRI_CCSS)
 struct sig_pri_cc_agent_prv {
 	/*! Asterisk span D channel control structure. */
@@ -754,15 +745,15 @@ static void sig_pri_set_subaddress(struct ast_party_subaddress *ast_subaddress,
 		ptr = cnum;
 		len = pri_subaddress->length - 1; /* -1 account for zero based indexing */
 		for (x = 0; x < len; ++x) {
-			ptr += sprintf(ptr, "%02x", (unsigned)pri_subaddress->data[x]);
+			ptr += sprintf(ptr, "%02hhx", (unsigned char)pri_subaddress->data[x]);
 		}
 
 		if (pri_subaddress->odd_even_indicator) {
 			/* ODD */
-			sprintf(ptr, "%01x", (unsigned)((pri_subaddress->data[len]) >> 4));
+			sprintf(ptr, "%01hhx", (unsigned char)((pri_subaddress->data[len]) >> 4));
 		} else {
 			/* EVEN */
-			sprintf(ptr, "%02x", (unsigned)pri_subaddress->data[len]);
+			sprintf(ptr, "%02hhx", (unsigned char)pri_subaddress->data[len]);
 		}
 		ast_subaddress->str = cnum;
 	}
@@ -1376,10 +1367,9 @@ static void sig_pri_queue_unhold(struct sig_pri_span *pri, int chanpos)
 static void pri_queue_control(struct sig_pri_span *pri, int chanpos, int subclass)
 {
 	struct ast_frame f = {AST_FRAME_CONTROL, };
-	struct sig_pri_chan *p = pri->pvts[chanpos];
 
 	if (sig_pri_callbacks.queue_control) {
-		sig_pri_callbacks.queue_control(p->chan_pvt, subclass);
+		sig_pri_callbacks.queue_control(pri->pvts[chanpos]->chan_pvt, subclass);
 	}
 
 	f.subclass.integer = subclass;
@@ -1388,6 +1378,31 @@ static void pri_queue_control(struct sig_pri_span *pri, int chanpos, int subclas
 
 /*!
  * \internal
+ * \brief Queue a request to hangup control frame onto the owner channel.
+ *
+ * \param pri PRI span control structure.
+ * \param chanpos Channel position in the span.
+ *
+ * \note Assumes the pri->lock is already obtained.
+ * \note Assumes the sig_pri_lock_private(pri->pvts[chanpos]) is already obtained.
+ *
+ * \return Nothing
+ */
+static void sig_pri_queue_hangup(struct sig_pri_span *pri, int chanpos)
+{
+	if (sig_pri_callbacks.queue_control) {
+		sig_pri_callbacks.queue_control(pri->pvts[chanpos]->chan_pvt, AST_CONTROL_HANGUP);
+	}
+
+	sig_pri_lock_owner(pri, chanpos);
+	if (pri->pvts[chanpos]->owner) {
+		ast_queue_hangup(pri->pvts[chanpos]->owner);
+		ast_channel_unlock(pri->pvts[chanpos]->owner);
+	}
+}
+
+/*!
+ * \internal
  * \brief Queue a PVT_CAUSE_CODE frame onto the owner channel.
  * \since 11
  *
@@ -4037,14 +4052,14 @@ static void sig_pri_send_aoce_termination_request(struct sig_pri_span *pri, int
 	}
 
 	if (!(decoded = ast_aoc_create(AST_AOC_REQUEST, 0, AST_AOC_REQUEST_E))) {
-		ast_softhangup_nolock(pvt->owner, AST_SOFTHANGUP_DEV);
+		ast_queue_hangup(pvt->owner);
 		goto cleanup_termination_request;
 	}
 
 	ast_aoc_set_termination_request(decoded);
 
 	if (!(encoded = ast_aoc_encode(decoded, &encoded_size, pvt->owner))) {
-		ast_softhangup_nolock(pvt->owner, AST_SOFTHANGUP_DEV);
+		ast_queue_hangup(pvt->owner);
 		goto cleanup_termination_request;
 	}
 
@@ -4053,7 +4068,7 @@ static void sig_pri_send_aoce_termination_request(struct sig_pri_span *pri, int
 	whentohangup.tv_sec = ms / 1000;
 
 	if (ast_queue_control_data(pvt->owner, AST_CONTROL_AOC, encoded, encoded_size)) {
-		ast_softhangup_nolock(pvt->owner, AST_SOFTHANGUP_DEV);
+		ast_queue_hangup(pvt->owner);
 		goto cleanup_termination_request;
 	}
 
@@ -4297,43 +4312,6 @@ static void sig_pri_handle_cis_subcmds(struct sig_pri_span *pri, int event_id,
 	}
 }
 
-#if defined(HAVE_PRI_AOC_EVENTS)
-/*!
- * \internal
- * \brief detect if AOC-S subcmd is present.
- * \since 1.8
- *
- * \param subcmds Subcommands to process if any. (Could be NULL).
- *
- * \note Knowing whether or not an AOC-E subcmd is present on certain
- * PRI hangup events is necessary to determine what method to use to hangup
- * the ast_channel.  If an AOC-E subcmd just came in, then a new AOC-E was queued
- * on the ast_channel.  If a soft hangup is used, the AOC-E msg will never make it
- * across the bridge, but if a AST_CONTROL_HANGUP frame is queued behind it
- * we can ensure the AOC-E frame makes it to it's destination before the hangup
- * frame is read.
- *
- *
- * \retval 0 AOC-E is not present in subcmd list
- * \retval 1 AOC-E is present in subcmd list
- */
-static int detect_aoc_e_subcmd(const struct pri_subcommands *subcmds)
-{
-	int i;
-
-	if (!subcmds) {
-		return 0;
-	}
-	for (i = 0; i < subcmds->counter_subcmd; ++i) {
-		const struct pri_subcommand *subcmd = &subcmds->subcmd[i];
-		if (subcmd->cmd == PRI_SUBCMD_AOC_E) {
-			return 1;
-		}
-	}
-	return 0;
-}
-#endif	/* defined(HAVE_PRI_AOC_EVENTS) */
-
 /*!
  * \internal
  * \brief Handle the call associated PRI subcommand events.
@@ -4704,7 +4682,7 @@ static void sig_pri_handle_subcmds(struct sig_pri_span *pri, int chanpos, int ev
 					f.frametype = AST_FRAME_TEXT;
 					f.subclass.integer = 0;
 					f.offset = 0;
-					f.data.ptr = &subcmd->u.display.text;
+					f.data.ptr = (void *)&subcmd->u.display.text;
 					f.datalen = subcmd->u.display.length + 1;
 					ast_queue_frame(owner, &f);
 					ast_channel_unlock(owner);
@@ -6579,9 +6557,8 @@ static void *pri_dchannel(void *vpri)
 								pri->pvts[chanpos]->call = NULL;
 							}
 						}
-						/* Force soft hangup if appropriate */
-						if (pri->pvts[chanpos]->owner)
-							ast_channel_softhangup_internal_flag_add(pri->pvts[chanpos]->owner, AST_SOFTHANGUP_DEV);
+						/* Force hangup if appropriate */
+						sig_pri_queue_hangup(pri, chanpos);
 						sig_pri_unlock_private(pri->pvts[chanpos]);
 					}
 				} else {
@@ -6593,8 +6570,8 @@ static void *pri_dchannel(void *vpri)
 								pri_destroycall(pri->pri, pri->pvts[x]->call);
 								pri->pvts[x]->call = NULL;
 							}
- 							if (pri->pvts[x]->owner)
-								ast_channel_softhangup_internal_flag_add(pri->pvts[x]->owner, AST_SOFTHANGUP_DEV);
+							/* Force hangup if appropriate */
+							sig_pri_queue_hangup(pri, x);
 							sig_pri_unlock_private(pri->pvts[x]);
 						}
 				}
@@ -7125,10 +7102,11 @@ static void *pri_dchannel(void *vpri)
 						break;
 					}
 					if (pri->pvts[chanpos]->owner) {
-						int do_hangup = 0;
-
 						snprintf(cause_str, sizeof(cause_str), "PRI PRI_EVENT_HANGUP (%d)", e->hangup.cause);
 						pri_queue_pvt_cause_data(pri, chanpos, cause_str, e->hangup.cause);
+					}
+					if (pri->pvts[chanpos]->owner) {
+						int do_hangup = 0;
 
 						/* Queue a BUSY instead of a hangup if our cause is appropriate */
 						ast_channel_hangupcause_set(pri->pvts[chanpos]->owner, e->hangup.cause);
@@ -7166,17 +7144,7 @@ static void *pri_dchannel(void *vpri)
 						}
 
 						if (do_hangup) {
-#if defined(HAVE_PRI_AOC_EVENTS)
-							if (detect_aoc_e_subcmd(e->hangup.subcmds)) {
-								/* If a AOC-E msg was sent during the release, we must use a
-								 * AST_CONTROL_HANGUP frame to guarantee that frame gets read before hangup */
-								pri_queue_control(pri, chanpos, AST_CONTROL_HANGUP);
-							} else {
-								ast_channel_softhangup_internal_flag_add(pri->pvts[chanpos]->owner, AST_SOFTHANGUP_DEV);
-							}
-#else
-							ast_channel_softhangup_internal_flag_add(pri->pvts[chanpos]->owner, AST_SOFTHANGUP_DEV);
-#endif	/* defined(HAVE_PRI_AOC_EVENTS) */
+							sig_pri_queue_hangup(pri, chanpos);
 						}
 					} else {
 						/*
@@ -7194,9 +7162,9 @@ static void *pri_dchannel(void *vpri)
 					pri_hangup(pri->pri, pri->pvts[chanpos]->call, e->hangup.cause);
 					pri->pvts[chanpos]->call = NULL;
 				}
-#if defined(FORCE_RESTART_UNAVAIL_CHANS)
 				if (e->hangup.cause == PRI_CAUSE_REQUESTED_CHAN_UNAVAIL
 					&& pri->sig != SIG_BRI_PTMP && !pri->resetting
+					&& pri->force_restart_unavailable_chans
 					&& pri->pvts[chanpos]->resetting == SIG_PRI_RESET_IDLE) {
 					ast_verb(3,
 						"Span %d: Forcing restart of channel %d/%d since channel reported in use\n",
@@ -7205,7 +7173,6 @@ static void *pri_dchannel(void *vpri)
 					pri->pvts[chanpos]->resetting = SIG_PRI_RESET_ACTIVE;
 					pri_reset(pri->pri, PVT_TO_CHANNEL(pri->pvts[chanpos]));
 				}
-#endif	/* defined(FORCE_RESTART_UNAVAIL_CHANS) */
 				if (e->hangup.aoc_units > -1)
 					ast_verb(3, "Channel %d/%d, span %d received AOC-E charging %d unit%s\n",
 						pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span, (int)e->hangup.aoc_units, (e->hangup.aoc_units == 1) ? "" : "s");
@@ -7280,10 +7247,11 @@ static void *pri_dchannel(void *vpri)
 					break;
 				}
 				if (pri->pvts[chanpos]->owner) {
-					int do_hangup = 0;
-
 					snprintf(cause_str, sizeof(cause_str), "PRI PRI_EVENT_HANGUP_REQ (%d)", e->hangup.cause);
 					pri_queue_pvt_cause_data(pri, chanpos, cause_str, e->hangup.cause);
+				}
+				if (pri->pvts[chanpos]->owner) {
+					int do_hangup = 0;
 
 					ast_channel_hangupcause_set(pri->pvts[chanpos]->owner, e->hangup.cause);
 					switch (ast_channel_state(pri->pvts[chanpos]->owner)) {
@@ -7326,16 +7294,11 @@ static void *pri_dchannel(void *vpri)
 							&& ast_channel_is_bridged(pri->pvts[chanpos]->owner)) {
 							sig_pri_send_aoce_termination_request(pri, chanpos,
 								pri_get_timer(pri->pri, PRI_TIMER_T305) / 2);
-						} else if (detect_aoc_e_subcmd(e->hangup.subcmds)) {
-							/* If a AOC-E msg was sent during the Disconnect, we must use a AST_CONTROL_HANGUP frame
-							 * to guarantee that frame gets read before hangup */
-							pri_queue_control(pri, chanpos, AST_CONTROL_HANGUP);
-						} else {
-							ast_channel_softhangup_internal_flag_add(pri->pvts[chanpos]->owner, AST_SOFTHANGUP_DEV);
-						}
-#else
-						ast_channel_softhangup_internal_flag_add(pri->pvts[chanpos]->owner, AST_SOFTHANGUP_DEV);
+						} else
 #endif	/* defined(HAVE_PRI_AOC_EVENTS) */
+						{
+							sig_pri_queue_hangup(pri, chanpos);
+						}
 					}
 					ast_verb(3, "Span %d: Channel %d/%d got hangup request, cause %d\n",
 						pri->span, pri->pvts[chanpos]->logicalspan,
@@ -7348,9 +7311,9 @@ static void *pri_dchannel(void *vpri)
 					pri_hangup(pri->pri, pri->pvts[chanpos]->call, e->hangup.cause);
 					pri->pvts[chanpos]->call = NULL;
 				}
-#if defined(FORCE_RESTART_UNAVAIL_CHANS)
 				if (e->hangup.cause == PRI_CAUSE_REQUESTED_CHAN_UNAVAIL
 					&& pri->sig != SIG_BRI_PTMP && !pri->resetting
+					&& pri->force_restart_unavailable_chans
 					&& pri->pvts[chanpos]->resetting == SIG_PRI_RESET_IDLE) {
 					ast_verb(3,
 						"Span %d: Forcing restart of channel %d/%d since channel reported in use\n",
@@ -7359,7 +7322,6 @@ static void *pri_dchannel(void *vpri)
 					pri->pvts[chanpos]->resetting = SIG_PRI_RESET_ACTIVE;
 					pri_reset(pri->pri, PVT_TO_CHANNEL(pri->pvts[chanpos]));
 				}
-#endif	/* defined(FORCE_RESTART_UNAVAIL_CHANS) */
 
 #ifdef SUPPORT_USERUSER
 				if (!ast_strlen_zero(e->hangup.useruserinfo)) {
@@ -8083,7 +8045,7 @@ int sig_pri_call(struct sig_pri_chan *p, struct ast_channel *ast, const char *rd
 	if (p->pri->facilityenable)
 		pri_facility_enable(p->pri->pri);
 
-	ast_verb(3, "Requested transfer capability: 0x%.2x - %s\n", (unsigned)ast_channel_transfercapability(ast), ast_transfercapability2str(ast_channel_transfercapability(ast)));
+	ast_verb(3, "Requested transfer capability: 0x%02hx - %s\n", ast_channel_transfercapability(ast), ast_transfercapability2str(ast_channel_transfercapability(ast)));
 	dp_strip = 0;
 	pridialplan = p->pri->dialplan - 1;
 	if (pridialplan == -2 || pridialplan == -3) { /* compute dynamically */
@@ -8632,16 +8594,18 @@ int sig_pri_indicate(struct sig_pri_chan *p, struct ast_channel *chan, int condi
 					if (p->pri->aoc_passthrough_flag & SIG_PRI_AOC_GRANT_E) {
 						sig_pri_aoc_e_from_ast(p, decoded);
 					}
-					/* if hangup was delayed for this AOC-E msg, waiting_for_aoc
+					/*
+					 * If hangup was delayed for this AOC-E msg, waiting_for_aoc
 					 * will be set.  A hangup is already occuring via a timeout during
 					 * this delay.  Instead of waiting for that timeout to occur, go ahead
-					 * and initiate the softhangup since the delay is no longer necessary */
+					 * and initiate the hangup since the delay is no longer necessary.
+					 */
 					if (p->waiting_for_aoce) {
 						p->waiting_for_aoce = 0;
 						ast_debug(1,
 							"Received final AOC-E msg, continue with hangup on %s\n",
 							ast_channel_name(chan));
-						ast_softhangup_nolock(chan, AST_SOFTHANGUP_DEV);
+						ast_queue_hangup(chan);
 					}
 					break;
 				case AST_AOC_REQUEST:
@@ -9027,7 +8991,7 @@ void sig_pri_stop_pri(struct sig_pri_span *pri)
 #if defined(HAVE_PRI_MWI)
 	for (idx = 0; idx < ARRAY_LEN(pri->mbox); ++idx) {
 		if (pri->mbox[idx].sub) {
-			pri->mbox[idx].sub = stasis_unsubscribe(pri->mbox[idx].sub);
+			pri->mbox[idx].sub = stasis_unsubscribe_and_join(pri->mbox[idx].sub);
 		}
 	}
 #endif	/* defined(HAVE_PRI_MWI) */
diff --git a/channels/sig_pri.h b/channels/sig_pri.h
index 12f3dca..d3e5350 100644
--- a/channels/sig_pri.h
+++ b/channels/sig_pri.h
@@ -497,6 +497,8 @@ struct sig_pri_span {
 	/*! \brief TRUE if allow sending MCID request on this span. */
 	unsigned int mcid_send:1;
 #endif	/* defined(HAVE_PRI_MCID) */
+	/*! \brief TRUE if forcing RESTART when receive cause 44 on this span. */
+	unsigned int force_restart_unavailable_chans:1;
 #if defined(HAVE_PRI_DATETIME_SEND)
 	/*! \brief Configured date/time ie send policy option. */
 	int datetime_send;
diff --git a/channels/sip/config_parser.c b/channels/sip/config_parser.c
index 3546cb4..5049542 100644
--- a/channels/sip/config_parser.c
+++ b/channels/sip/config_parser.c
@@ -25,7 +25,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420562 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "include/sip.h"
 #include "include/config_parser.h"
@@ -79,13 +79,17 @@ int sip_parse_register_line(struct sip_registry *reg, int default_expiry, const
 		AST_APP_ARG(port);
 	);
 
-	if (!value) {
+	if (!reg) {
 		return -1;
 	}
 
-	if (!reg) {
+	reg->expire = -1;
+	reg->timeout = -1;
+
+	if (!value) {
 		return -1;
 	}
+
 	ast_copy_string(buf, value, sizeof(buf));
 
 	/*! register => [peer?][transport://]user[@domain][:secret[:authuser]]@host[:port][/extension][~expiry]
@@ -261,7 +265,6 @@ int sip_parse_register_line(struct sip_registry *reg, int default_expiry, const
 	ast_string_field_set(reg, regdomain, ast_strip_quoted(S_OR(user2.domain, ""), "\"", "\""));
 
 	reg->transport = transport;
-	reg->timeout = reg->expire = -1;
 	reg->portno = portnum;
 	reg->regdomainport = domainport;
 	reg->callid_valid = FALSE;
diff --git a/channels/sip/dialplan_functions.c b/channels/sip/dialplan_functions.c
index e8c2e15..5499db8 100644
--- a/channels/sip/dialplan_functions.c
+++ b/channels/sip/dialplan_functions.c
@@ -25,7 +25,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420562 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <math.h>
 
@@ -386,7 +386,7 @@ AST_TEST_DEFINE(test_sip_rtpqos_1)
 			for (j = 1.0; j < 10.0; j += 0.3) {
 				*lookup[i].d8 = j;
 				ast_str_substitute_variables(&buffer, 0, chan, ast_str_buffer(varstr));
-				if (sscanf(ast_str_buffer(buffer), "%lf", &cmpdbl) != 1 || fabs(j - cmpdbl > .05)) {
+				if (sscanf(ast_str_buffer(buffer), "%lf", &cmpdbl) != 1 || fabs(j - cmpdbl) > .05) {
 					res = AST_TEST_FAIL;
 					ast_test_status_update(test, "%s != %f != %s\n", ast_str_buffer(varstr), j, ast_str_buffer(buffer));
 					break;
diff --git a/channels/sip/include/dialog.h b/channels/sip/include/dialog.h
index ea2fb45..582841d 100644
--- a/channels/sip/include/dialog.h
+++ b/channels/sip/include/dialog.h
@@ -29,13 +29,16 @@
  * functions so we keep track of the refcounts.
  * To simplify the code, we allow a NULL to be passed to dialog_unref().
  */
-#define dialog_ref(arg1,arg2) dialog_ref_debug((arg1),(arg2), __FILE__, __LINE__, __PRETTY_FUNCTION__)
-#define dialog_unref(arg1,arg2) dialog_unref_debug((arg1),(arg2), __FILE__, __LINE__, __PRETTY_FUNCTION__)
-struct sip_pvt *dialog_ref_debug(struct sip_pvt *p, const char *tag, char *file, int line, const char *func);
-struct sip_pvt *dialog_unref_debug(struct sip_pvt *p, const char *tag, char *file, int line, const char *func);
+#define dialog_ref(dialog, tag) ao2_t_bump(dialog, tag)
+#define dialog_unref(dialog, tag) ({ ao2_t_cleanup(dialog, tag); (NULL); })
+
+struct sip_pvt *__sip_alloc(ast_string_field callid, struct ast_sockaddr *sin,
+				 int useglobal_nat, const int intended_method, struct sip_request *req, struct ast_callid *logger_callid,
+				 const char *file, int line, const char *func);
+
+#define sip_alloc(callid, addr, useglobal_nat, intended_method, req, logger_callid) \
+	__sip_alloc(callid, addr, useglobal_nat, intended_method, req, logger_callid, __FILE__, __LINE__, __PRETTY_FUNCTION__)
 
-struct sip_pvt *sip_alloc(ast_string_field callid, struct ast_sockaddr *sin,
-				 int useglobal_nat, const int intended_method, struct sip_request *req, struct ast_callid *logger_callid);
 void sip_scheddestroy_final(struct sip_pvt *p, int ms);
 void sip_scheddestroy(struct sip_pvt *p, int ms);
 int sip_cancel_destroy(struct sip_pvt *p);
diff --git a/channels/sip/include/route.h b/channels/sip/include/route.h
index 511f0ff..3221cce 100644
--- a/channels/sip/include/route.h
+++ b/channels/sip/include/route.h
@@ -98,7 +98,7 @@ void sip_route_dump(const struct sip_route *route);
  * \retval NULL on failure
  */
 struct ast_str *sip_route_list(const struct sip_route *route, int formatcli, int skip)
-		__attribute_malloc__ __attribute_warn_unused_result__;
+	__attribute__((__malloc__)) __attribute__((__warn_unused_result__));
 
 /*!
  * \brief Check if the route is strict
diff --git a/channels/sip/include/security_events.h b/channels/sip/include/security_events.h
index cee2fa7..1d0f58b 100644
--- a/channels/sip/include/security_events.h
+++ b/channels/sip/include/security_events.h
@@ -38,6 +38,7 @@ void sip_report_failed_challenge_response(const struct sip_pvt *p, const char *r
 void sip_report_chal_sent(const struct sip_pvt *p);
 void sip_report_inval_transport(const struct sip_pvt *p, const char *transport);
 void sip_digest_parser(char *c, struct digestkeys *keys);
-int sip_report_security_event(const struct sip_pvt *p, const struct sip_request *req, const int res);
+int sip_report_security_event(const char *peer, struct ast_sockaddr *addr, const struct sip_pvt *p,
+			      const struct sip_request *req, const int res);
 
 #endif
diff --git a/channels/sip/include/sip.h b/channels/sip/include/sip.h
index 68c2ee8..82f208c 100644
--- a/channels/sip/include/sip.h
+++ b/channels/sip/include/sip.h
@@ -291,8 +291,8 @@
 
 /* Sending PROGRESS in-band settings */
 #define SIP_PROG_INBAND        (3 << 25) /*!< DP: three settings, uses two bits */
-#define SIP_PROG_INBAND_NEVER  (0 << 25)
-#define SIP_PROG_INBAND_NO     (1 << 25)
+#define SIP_PROG_INBAND_NO     (0 << 25)
+#define SIP_PROG_INBAND_NEVER  (1 << 25)
 #define SIP_PROG_INBAND_YES    (2 << 25)
 
 #define SIP_USEPATH          (1 << 27) /*!< GDP: Trust and use incoming Path headers? */
@@ -774,6 +774,7 @@ struct sip_settings {
 	int tcp_enabled;
 	int default_max_forwards;    /*!< Default max forwards (SIP Anti-loop) */
 	int websocket_write_timeout; /*!< Socket write timeout for websocket transports, in ms */
+	int websocket_enabled;       /*!< Are websockets enabled? */
 };
 
 struct ast_websocket;
@@ -1869,14 +1870,7 @@ void sip_auth_headers(enum sip_auth_type code, char **header, char **respheader)
 const char *sip_get_header(const struct sip_request *req, const char *name);
 const char *sip_get_transport(enum ast_transport t);
 
-#ifdef REF_DEBUG
-#define sip_ref_peer(arg1,arg2) _ref_peer((arg1),(arg2), __FILE__, __LINE__, __PRETTY_FUNCTION__)
-#define sip_unref_peer(arg1,arg2) _unref_peer((arg1),(arg2), __FILE__, __LINE__, __PRETTY_FUNCTION__)
-struct sip_peer *_ref_peer(struct sip_peer *peer, char *tag, char *file, int line, const char *func);
-void *_unref_peer(struct sip_peer *peer, char *tag, char *file, int line, const char *func);
-#else
-struct sip_peer *sip_ref_peer(struct sip_peer *peer, char *tag);
-void *sip_unref_peer(struct sip_peer *peer, char *tag);
-#endif /* REF_DEBUG */
+#define sip_ref_peer(peer, tag) ao2_t_bump(peer, tag)
+#define sip_unref_peer(peer, tag) ({ ao2_t_cleanup(peer, tag); (NULL); })
 
 #endif
diff --git a/channels/sip/reqresp_parser.c b/channels/sip/reqresp_parser.c
index 39e43f2..31832a3 100644
--- a/channels/sip/reqresp_parser.c
+++ b/channels/sip/reqresp_parser.c
@@ -25,7 +25,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 426865 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "include/sip.h"
 #include "include/sip_utils.h"
@@ -2420,7 +2420,7 @@ struct sip_via *parse_via(const char *header)
 
 	/* store the port, we have to handle ipv6 addresses containing ':'
 	 * characters gracefully */
-	if (((parm = strchr(v->sent_by, ']')) && *(++parm) == ':') || (parm = strchr(v->sent_by, ':'))) {
+	if (((parm = strchr(v->sent_by, ']')) && *(++parm) == ':') || (!(parm = strchr(v->sent_by, ']')) && (parm = strchr(v->sent_by, ':')))) {
 		char *endptr;
 
 		v->port = strtol(++parm, &endptr, 10);
diff --git a/channels/sip/route.c b/channels/sip/route.c
index fea5ab3..14c2b44 100644
--- a/channels/sip/route.c
+++ b/channels/sip/route.c
@@ -25,7 +25,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420562 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/utils.h"
 
diff --git a/channels/sip/security_events.c b/channels/sip/security_events.c
index 293475e..9f5d94f 100644
--- a/channels/sip/security_events.c
+++ b/channels/sip/security_events.c
@@ -30,7 +30,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428246 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "include/sip.h"
 #include "include/security_events.h"
@@ -270,7 +270,8 @@ void sip_report_inval_transport(const struct sip_pvt *p, const char *transport)
         ast_security_event_report(AST_SEC_EVT(&inval_transport));
 }
 
-int sip_report_security_event(const struct sip_pvt *p, const struct sip_request *req, const int res) {
+int sip_report_security_event(const char *peer, struct ast_sockaddr *addr, const struct sip_pvt *p,
+			      const struct sip_request *req, const int res) {
 
 	struct sip_peer *peer_report;
 	enum check_auth_result res_report = res;
@@ -288,7 +289,7 @@ int sip_report_security_event(const struct sip_pvt *p, const struct sip_request
 		[K_LAST]  = { NULL, NULL}
 	};
 
-	peer_report = sip_find_peer(p->exten, NULL, TRUE, FINDPEERS, FALSE, 0);
+	peer_report = sip_find_peer(peer, addr, TRUE, FINDPEERS, FALSE, p->socket.type);
 
 	switch(res_report) {
 	case AUTH_DONT_KNOW:
diff --git a/channels/vcodecs.c b/channels/vcodecs.c
index 5f9cb1a..76aeb67 100644
--- a/channels/vcodecs.c
+++ b/channels/vcodecs.c
@@ -16,7 +16,7 @@
 
 /*
  * Video codecs support for console_video.c
- * $Revision: 369013 $
+ * $Revision$
  */
 
 /*** MODULEINFO
@@ -209,9 +209,9 @@ void dump_buf(struct fbuf_t *b)
 			if (i != 0)
 				ast_log(LOG_WARNING, "%s\n", buf);
 			memset(buf, '\0', sizeof(buf));
-			sprintf(buf, "%04x: ", i);
+			sprintf(buf, "%04x: ", (unsigned)i);
 		}
-		sprintf(buf + 6 + x*3, "%02x ", b->data[i]);
+		sprintf(buf + 6 + x*3, "%02hhx ", b->data[i]);
 		if (i > 31 && i < last2lines)
 			i = last2lines - 1;
 	}
@@ -1056,7 +1056,7 @@ static int h264_decap(struct fbuf_t *b, uint8_t *data, int len)
 	}
 	/* first of all, check if the packet has F == 0 */
 	if (data[0] & 0x80) {
-		ast_log(LOG_WARNING, "--- forbidden packet; nal: %02x\n",
+		ast_log(LOG_WARNING, "--- forbidden packet; nal: %02hhx\n",
 			data[0]);
 		return 1;
 	}
diff --git a/channels/vgrabbers.c b/channels/vgrabbers.c
index 7374acc..45dced4 100644
--- a/channels/vgrabbers.c
+++ b/channels/vgrabbers.c
@@ -17,7 +17,7 @@
 /*
  * Video grabbers used in console_video.
  *
- * $Revision: 369013 $
+ * $Revision$
  *
  * Each grabber is implemented through open/read/close calls,
  * plus an additional move() function used e.g. to change origin
@@ -49,7 +49,7 @@
  ***/
 
 #include "asterisk.h"
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 369013 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include <sys/ioctl.h>
 #include "asterisk/file.h"
 #include "asterisk/utils.h"	/* ast_calloc */
diff --git a/codecs/codec_a_mu.c b/codecs/codec_a_mu.c
index 2382f97..c21c706 100644
--- a/codecs/codec_a_mu.c
+++ b/codecs/codec_a_mu.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419044 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/translate.h"
diff --git a/codecs/codec_adpcm.c b/codecs/codec_adpcm.c
index ec80cac..6b64f86 100644
--- a/codecs/codec_adpcm.c
+++ b/codecs/codec_adpcm.c
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/lock.h"
 #include "asterisk/linkedlists.h"
diff --git a/codecs/codec_alaw.c b/codecs/codec_alaw.c
index e53d3f0..de9c4c4 100644
--- a/codecs/codec_alaw.c
+++ b/codecs/codec_alaw.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/config.h"
diff --git a/codecs/codec_dahdi.c b/codecs/codec_dahdi.c
index 0ba3737..8832bc3 100644
--- a/codecs/codec_dahdi.c
+++ b/codecs/codec_dahdi.c
@@ -34,7 +34,7 @@
 #include "asterisk.h"
 #include <stdbool.h>
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 426097 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <fcntl.h>
 #include <netinet/in.h>
diff --git a/codecs/codec_g722.c b/codecs/codec_g722.c
index f423034..e262ebd 100644
--- a/codecs/codec_g722.c
+++ b/codecs/codec_g722.c
@@ -39,7 +39,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/linkedlists.h"
 #include "asterisk/module.h"
diff --git a/codecs/codec_g726.c b/codecs/codec_g726.c
index 6d75d3f..1954c51 100644
--- a/codecs/codec_g726.c
+++ b/codecs/codec_g726.c
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/lock.h"
 #include "asterisk/linkedlists.h"
diff --git a/codecs/codec_gsm.c b/codecs/codec_gsm.c
index 047a89f..a18dc0a 100644
--- a/codecs/codec_gsm.c
+++ b/codecs/codec_gsm.c
@@ -33,12 +33,13 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/translate.h"
 #include "asterisk/config.h"
 #include "asterisk/module.h"
 #include "asterisk/utils.h"
+#include "asterisk/linkedlists.h"
 
 #ifdef HAVE_GSM_HEADER
 #include "gsm.h"
@@ -139,25 +140,35 @@ static int lintogsm_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
 static struct ast_frame *lintogsm_frameout(struct ast_trans_pvt *pvt)
 {
 	struct gsm_translator_pvt *tmp = pvt->pvt;
-	int datalen = 0;
-	int samples = 0;
+	struct ast_frame *result = NULL;
+	struct ast_frame *last = NULL;
+	int samples = 0; /* output samples */
 
-	/* We can't work on anything less than a frame in size */
-	if (pvt->samples < GSM_SAMPLES)
-		return NULL;
 	while (pvt->samples >= GSM_SAMPLES) {
+		struct ast_frame *current;
+
 		/* Encode a frame of data */
-		gsm_encode(tmp->gsm, tmp->buf + samples, (gsm_byte *) pvt->outbuf.c + datalen);
-		datalen += GSM_FRAME_LEN;
+		gsm_encode(tmp->gsm, tmp->buf + samples, (gsm_byte *) pvt->outbuf.c);
 		samples += GSM_SAMPLES;
 		pvt->samples -= GSM_SAMPLES;
+
+		current = ast_trans_frameout(pvt, GSM_FRAME_LEN, GSM_SAMPLES);
+		if (!current) {
+			continue;
+		} else if (last) {
+			AST_LIST_NEXT(last, frame_list) = current;
+		} else {
+			result = current;
+		}
+		last = current;
 	}
 
 	/* Move the data at the end of the buffer to the front */
-	if (pvt->samples)
+	if (samples) {
 		memmove(tmp->buf, tmp->buf + samples, pvt->samples * 2);
+	}
 
-	return ast_trans_frameout(pvt, datalen, samples);
+	return result;
 }
 
 static void gsm_destroy_stuff(struct ast_trans_pvt *pvt)
diff --git a/codecs/codec_ilbc.c b/codecs/codec_ilbc.c
index 75f8195..2646f49 100644
--- a/codecs/codec_ilbc.c
+++ b/codecs/codec_ilbc.c
@@ -32,11 +32,12 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419044 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/translate.h"
 #include "asterisk/module.h"
 #include "asterisk/utils.h"
+#include "asterisk/linkedlists.h"
 
 #ifdef ILBC_WEBRTC
 #include <ilbc.h>
@@ -150,31 +151,40 @@ static int lintoilbc_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
 static struct ast_frame *lintoilbc_frameout(struct ast_trans_pvt *pvt)
 {
 	struct ilbc_coder_pvt *tmp = pvt->pvt;
-	int datalen = 0;
-	int samples = 0;
+	struct ast_frame *result = NULL;
+	struct ast_frame *last = NULL;
+	int samples = 0; /* output samples */
 
-	/* We can't work on anything less than a frame in size */
-	if (pvt->samples < ILBC_SAMPLES)
-		return NULL;
 	while (pvt->samples >= ILBC_SAMPLES) {
+		struct ast_frame *current;
 		ilbc_block tmpf[ILBC_SAMPLES];
 		int i;
 
 		/* Encode a frame of data */
 		for (i = 0 ; i < ILBC_SAMPLES ; i++)
 			tmpf[i] = tmp->buf[samples + i];
-		iLBC_encode( (ilbc_bytes*)pvt->outbuf.BUF_TYPE + datalen, tmpf, &tmp->enc);
+		iLBC_encode((ilbc_bytes *) pvt->outbuf.BUF_TYPE, tmpf, &tmp->enc);
 
-		datalen += ILBC_FRAME_LEN;
 		samples += ILBC_SAMPLES;
 		pvt->samples -= ILBC_SAMPLES;
+
+		current = ast_trans_frameout(pvt, ILBC_FRAME_LEN, ILBC_SAMPLES);
+		if (!current) {
+			continue;
+		} else if (last) {
+			AST_LIST_NEXT(last, frame_list) = current;
+		} else {
+			result = current;
+		}
+		last = current;
 	}
 
 	/* Move the data at the end of the buffer to the front */
-	if (pvt->samples)
+	if (samples) {
 		memmove(tmp->buf, tmp->buf + samples, pvt->samples * 2);
+	}
 
-	return ast_trans_frameout(pvt, datalen, samples);
+	return result;
 }
 
 static struct ast_translator ilbctolin = {
diff --git a/codecs/codec_lpc10.c b/codecs/codec_lpc10.c
index c9509ce..a62eed3 100644
--- a/codecs/codec_lpc10.c
+++ b/codecs/codec_lpc10.c
@@ -33,12 +33,13 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/translate.h"
 #include "asterisk/config.h"
 #include "asterisk/module.h"
 #include "asterisk/utils.h"
+#include "asterisk/linkedlists.h"
 
 #include "lpc10/lpc10.h"
 
@@ -160,31 +161,45 @@ static int lintolpc10_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
 static struct ast_frame *lintolpc10_frameout(struct ast_trans_pvt *pvt)
 {
 	struct lpc10_coder_pvt *tmp = pvt->pvt;
-	int x;
-	int datalen = 0;	/* output frame */
-	int samples = 0;	/* output samples */
-	float tmpbuf[LPC10_SAMPLES_PER_FRAME];
-	INT32 bits[LPC10_BITS_IN_COMPRESSED_FRAME];	/* XXX what ??? */
-	/* We can't work on anything less than a frame in size */
-	if (pvt->samples < LPC10_SAMPLES_PER_FRAME)
-		return NULL;
-	while (pvt->samples >=  LPC10_SAMPLES_PER_FRAME) {
+	struct ast_frame *result = NULL;
+	struct ast_frame *last = NULL;
+	int samples = 0; /* output samples */
+
+	while (pvt->samples >= LPC10_SAMPLES_PER_FRAME) {
+		struct ast_frame *current;
+		float tmpbuf[LPC10_SAMPLES_PER_FRAME];
+		INT32 bits[LPC10_BITS_IN_COMPRESSED_FRAME];	/* XXX what ??? */
+		int x;
+
 		/* Encode a frame of data */
 		for (x=0;x<LPC10_SAMPLES_PER_FRAME;x++)
 			tmpbuf[x] = (float)tmp->buf[x + samples] / 32768.0;
 		lpc10_encode(tmpbuf, bits, tmp->lpc10.enc);
-		build_bits(pvt->outbuf.uc + datalen, bits);
-		datalen += LPC10_BYTES_IN_COMPRESSED_FRAME;
+		build_bits(pvt->outbuf.uc, bits);
+
 		samples += LPC10_SAMPLES_PER_FRAME;
 		pvt->samples -= LPC10_SAMPLES_PER_FRAME;
 		/* Use one of the two left over bits to record if this is a 22 or 23 ms frame...
 		   important for IAX use */
 		tmp->longer = 1 - tmp->longer;
+
+		current = ast_trans_frameout(pvt, LPC10_BYTES_IN_COMPRESSED_FRAME, LPC10_SAMPLES_PER_FRAME);
+		if (!current) {
+			continue;
+		} else if (last) {
+			AST_LIST_NEXT(last, frame_list) = current;
+		} else {
+			result = current;
+		}
+		last = current;
 	}
+
 	/* Move the data at the end of the buffer to the front */
-	if (pvt->samples)
+	if (samples) {
 		memmove(tmp->buf, tmp->buf + samples, pvt->samples * 2);
-	return ast_trans_frameout(pvt, datalen, samples);
+	}
+
+	return result;
 }
 
 
diff --git a/codecs/codec_resample.c b/codecs/codec_resample.c
index dc6dfd2..9eda242 100644
--- a/codecs/codec_resample.c
+++ b/codecs/codec_resample.c
@@ -32,13 +32,13 @@
 #include "asterisk.h"
 #include "speex/speex_resampler.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419044 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/translate.h"
 #include "asterisk/slin.h"
 
-#define OUTBUF_SIZE   8096
+#define OUTBUF_SAMPLES   11520
 
 static struct ast_translator *translators;
 static int trans_size;
@@ -114,7 +114,7 @@ static void resamp_destroy(struct ast_trans_pvt *pvt)
 static int resamp_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
 {
 	SpeexResamplerState *resamp_pvt = pvt->pvt;
-	unsigned int out_samples = (OUTBUF_SIZE / sizeof(int16_t)) - pvt->samples;
+	unsigned int out_samples = OUTBUF_SAMPLES - pvt->samples;
 	unsigned int in_samples;
 
 	if (!f->datalen) {
@@ -167,8 +167,8 @@ static int load_module(void)
 			translators[idx].destroy = resamp_destroy;
 			translators[idx].framein = resamp_framein;
 			translators[idx].desc_size = 0;
-			translators[idx].buffer_samples = (OUTBUF_SIZE / sizeof(int16_t));
-			translators[idx].buf_size = OUTBUF_SIZE;
+			translators[idx].buffer_samples = OUTBUF_SAMPLES;
+			translators[idx].buf_size = (OUTBUF_SAMPLES * sizeof(int16_t));
 			memcpy(&translators[idx].src_codec, &codec_list[x], sizeof(struct ast_codec));
 			memcpy(&translators[idx].dst_codec, &codec_list[y], sizeof(struct ast_codec));
 			snprintf(translators[idx].name, sizeof(translators[idx].name), "slin %ukhz -> %ukhz",
diff --git a/codecs/codec_speex.c b/codecs/codec_speex.c
index 6bc8fcd..c9adceb 100644
--- a/codecs/codec_speex.c
+++ b/codecs/codec_speex.c
@@ -39,7 +39,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <speex/speex.h>
 
@@ -54,6 +54,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
 #include "asterisk/module.h"
 #include "asterisk/config.h"
 #include "asterisk/utils.h"
+#include "asterisk/frame.h"
+#include "asterisk/linkedlists.h"
 
 /* codec variables */
 static int quality = 3;
@@ -259,15 +261,16 @@ static int lintospeex_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
 static struct ast_frame *lintospeex_frameout(struct ast_trans_pvt *pvt)
 {
 	struct speex_coder_pvt *tmp = pvt->pvt;
-	int is_speech=1;
-	int datalen = 0;	/* output bytes */
-	int samples = 0;	/* output samples */
+	struct ast_frame *result = NULL;
+	struct ast_frame *last = NULL;
+	int samples = 0; /* output samples */
 
-	/* We can't work on anything less than a frame in size */
-	if (pvt->samples < tmp->framesize)
-		return NULL;
-	speex_bits_reset(&tmp->bits);
 	while (pvt->samples >= tmp->framesize) {
+		struct ast_frame *current;
+		int is_speech = 1;
+
+		speex_bits_reset(&tmp->bits);
+
 #ifdef _SPEEX_TYPES_H
 		/* Preprocess audio */
 		if (preproc)
@@ -293,18 +296,18 @@ static struct ast_frame *lintospeex_frameout(struct ast_trans_pvt *pvt)
 #endif
 		samples += tmp->framesize;
 		pvt->samples -= tmp->framesize;
-	}
 
-	/* Move the data at the end of the buffer to the front */
-	if (pvt->samples)
-		memmove(tmp->buf, tmp->buf + samples, pvt->samples * 2);
-
-	/* Use AST_FRAME_CNG to signify the start of any silence period */
-	if (is_speech) {
-		tmp->silent_state = 0;
-	} else {
-		if (tmp->silent_state) {
-			return NULL;
+		/* Use AST_FRAME_CNG to signify the start of any silence period */
+		if (is_speech) {
+			int datalen = 0; /* output bytes */
+
+			tmp->silent_state = 0;
+			/* Terminate bit stream */
+			speex_bits_pack(&tmp->bits, 15, 5);
+			datalen = speex_bits_write(&tmp->bits, pvt->outbuf.c, pvt->t->buf_size);
+			current = ast_trans_frameout(pvt, datalen, tmp->framesize);
+		} else if (tmp->silent_state) {
+			current = NULL;
 		} else {
 			struct ast_frame frm = {
 				.frametype = AST_FRAME_CNG,
@@ -320,14 +323,25 @@ static struct ast_frame *lintospeex_frameout(struct ast_trans_pvt *pvt)
 			tmp->silent_state = 1;
 
 			/* XXX what now ? format etc... */
-			return ast_frisolate(&frm);
+			current = ast_frisolate(&frm);
 		}
+
+		if (!current) {
+			continue;
+		} else if (last) {
+			AST_LIST_NEXT(last, frame_list) = current;
+		} else {
+			result = current;
+		}
+		last = current;
+	}
+
+	/* Move the data at the end of the buffer to the front */
+	if (samples) {
+		memmove(tmp->buf, tmp->buf + samples, pvt->samples * 2);
 	}
 
-	/* Terminate bit stream */
-	speex_bits_pack(&tmp->bits, 15, 5);
-	datalen = speex_bits_write(&tmp->bits, pvt->outbuf.c, pvt->t->buf_size);
-	return ast_trans_frameout(pvt, datalen, samples);
+	return result;
 }
 
 static void speextolin_destroy(struct ast_trans_pvt *arg)
diff --git a/codecs/codec_ulaw.c b/codecs/codec_ulaw.c
index 3ed8b40..58c0a89 100644
--- a/codecs/codec_ulaw.c
+++ b/codecs/codec_ulaw.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/config.h"
diff --git a/codecs/g722/g722.h b/codecs/g722/g722.h
index a197ffd..f57b1c8 100644
--- a/codecs/g722/g722.h
+++ b/codecs/g722/g722.h
@@ -18,7 +18,7 @@
  * Computer Science, Speech Group
  * Chengxiang Lu and Alex Hauptmann
  *
- * $Id: g722.h 48959 2006-12-25 06:42:15Z rizzo $
+ * $Id$
  */
 
 
diff --git a/codecs/g722/g722_decode.c b/codecs/g722/g722_decode.c
index 233bee5..3e8f7d0 100644
--- a/codecs/g722/g722_decode.c
+++ b/codecs/g722/g722_decode.c
@@ -18,7 +18,7 @@
  * Computer Science, Speech Group
  * Chengxiang Lu and Alex Hauptmann
  *
- * $Id: g722_decode.c 194722 2009-05-15 17:59:08Z russell $
+ * $Id$
  */
 
 /*! \file */
diff --git a/codecs/g722/g722_encode.c b/codecs/g722/g722_encode.c
index db563a7..5890fbf 100644
--- a/codecs/g722/g722_encode.c
+++ b/codecs/g722/g722_encode.c
@@ -20,7 +20,7 @@
  * Computer Science, Speech Group
  * Chengxiang Lu and Alex Hauptmann
  *
- * $Id: g722_encode.c 194722 2009-05-15 17:59:08Z russell $
+ * $Id$
  */
 
 /*! \file */
diff --git a/codecs/gsm/Makefile b/codecs/gsm/Makefile
index a072e6d..06f0829 100644
--- a/codecs/gsm/Makefile
+++ b/codecs/gsm/Makefile
@@ -315,7 +315,7 @@ install:	toastinstall gsminstall
 # The basic API: libgsm
 
 $(LIBGSMSO):	$(LIB) $(GSM_OBJECTS)
-		$(LD) -o $@.1.0.10 -shared -Xlinker -soname -Xlinker libgsm.so.1 $(GSM_OBJECTS) -lc
+		$(LD) -o $@.1.0.10 -shared -Wl,-soname,libgsm.so.1 $(GSM_OBJECTS) -lc
 		ln -fs libgsm.so.1.0.10 lib/libgsm.so.1
 		ln -fs libgsm.so.1.0.10 lib/libgsm.so
 
diff --git a/codecs/gsm/src/gsm_create.c b/codecs/gsm/src/gsm_create.c
index a59aa2f..f952a7d 100644
--- a/codecs/gsm/src/gsm_create.c
+++ b/codecs/gsm/src/gsm_create.c
@@ -4,8 +4,6 @@
  * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
  */
 
-static char const	ident[] = "$Header$";
-
 #include	"config.h"
 
 #ifdef	HAS_STRING_H
diff --git a/configs/basic-pbx/README b/configs/basic-pbx/README
new file mode 100644
index 0000000..0f57ad6
--- /dev/null
+++ b/configs/basic-pbx/README
@@ -0,0 +1,15 @@
+The included Asterisk configuration files are intended to be an example
+implementation for a fictitious company, Super Awesome Company.
+
+It can serve as a handy reference for understanding a simple Asterisk
+configuration in an approximate real-world environment.
+
+If you intend to use this configuration as a template for your own, then
+you will need to change many values in the various configuration files to
+match your own devices, network, SIP ITSP accounts and more.
+
+For further documentation on this configuration see the Asterisk wiki:
+https://wiki.asterisk.org/wiki/display/AST/Reference+Use+Cases+for+Asterisk.
+
+Please report bugs or errors in configuration on the Asterisk issue tracker:
+https://wiki.asterisk.org/wiki/display/AST/Asterisk+Issue+Guidelines
diff --git a/configs/basic-pbx/asterisk.conf b/configs/basic-pbx/asterisk.conf
new file mode 100644
index 0000000..3ee7b99
--- /dev/null
+++ b/configs/basic-pbx/asterisk.conf
@@ -0,0 +1,26 @@
+[directories]
+astetcdir = /etc/asterisk
+astmoddir = /usr/lib/asterisk/modules
+astvarlibdir = /var/lib/asterisk
+astdbdir = /var/lib/asterisk
+astkeydir = /var/lib/asterisk
+astdatadir = /var/lib/asterisk
+astagidir = /var/lib/asterisk/agi-bin
+astspooldir = /var/spool/asterisk
+astrundir = /var/run/asterisk
+astlogdir = /var/log/asterisk
+astsbindir = /usr/sbin
+
+[options]
+; If we want to start Asterisk with a default verbosity for the verbose
+; or debug logger channel types, then we use these settings.
+;verbose = 5
+;debug = 5
+
+; User and group to run asterisk as. NOTE: This will require changes to
+; directory and device permissions.
+;runuser = asterisk		; The user to run as.
+;rungroup = asterisk		; The group to run as.
+
+defaultlanguage = en
+documentation_language = en_US
diff --git a/configs/basic-pbx/cdr.conf b/configs/basic-pbx/cdr.conf
new file mode 100644
index 0000000..e8749e3
--- /dev/null
+++ b/configs/basic-pbx/cdr.conf
@@ -0,0 +1,7 @@
+[general]
+enable=yes
+
+[custom]
+; We log the unique ID as it can be useful for troubleshooting any issues
+; that arise.
+loguniqueid=yes
diff --git a/configs/basic-pbx/cdr_custom.conf b/configs/basic-pbx/cdr_custom.conf
new file mode 100644
index 0000000..12ad24f
--- /dev/null
+++ b/configs/basic-pbx/cdr_custom.conf
@@ -0,0 +1,4 @@
+[mappings]
+; Our CDR log will be written to /var/log/asterisk/cdr-custom/Master.csv
+; with the following schema.
+Master.csv => ${CSV_QUOTE(${CDR(clid)})},${CSV_QUOTE(${CDR(src)})},${CSV_QUOTE(${CDR(dst)})},${CSV_QUOTE(${CDR(dcontext)})},${CSV_QUOTE(${CDR(channel)})},${CSV_QUOTE(${CDR(dstchannel)})},${CSV_QUOTE(${CDR(lastapp)})},${CSV_QUOTE(${CDR(lastdata)})},${CSV_QUOTE(${CDR(start)})},${CSV_QUOTE(${CDR(answer)})},${CSV_QUOTE(${CDR(end)})},${CSV_QUOTE(${CDR(duration)})},${CSV_QUOTE(${CDR(billsec)})},${CSV_QUOTE(${CDR(disposition)})},${CSV_QUOTE(${CDR(amaflags)})},${CSV_QUOTE(${CDR(accountcode)})},$ [...]
diff --git a/configs/basic-pbx/confbridge.conf b/configs/basic-pbx/confbridge.conf
new file mode 100644
index 0000000..3a44e68
--- /dev/null
+++ b/configs/basic-pbx/confbridge.conf
@@ -0,0 +1 @@
+; All conferences use default settings. This config must be present to load the confbridge application
diff --git a/configs/basic-pbx/extensions.conf b/configs/basic-pbx/extensions.conf
new file mode 100644
index 0000000..9682e06
--- /dev/null
+++ b/configs/basic-pbx/extensions.conf
@@ -0,0 +1,193 @@
+[globals]
+; General internal dialing options used in context Dial-Users.
+; Only the timeout is defined here. See the Dial app documentation for
+; additional options.
+INTERNAL_DIAL_OPT=,30
+
+[Hints]
+; Allow dynamic hint creation for every extension.
+exten = _11XX,hint,PJSIP/${EXTEN}
+
+[Features]
+; Extension to check user voicemail. We don't requre the user to enter
+; their pincode.
+exten = 8000,1,Verbose(1, "User ${CALLERID(num)} dialed the voicemail feature.")
+ same = n,VoiceMailMain(${CALLERID(num)}@example,s)
+ same = n,Hangup()
+
+; Exten to dial the main IVR internally.
+exten = 1100,1,Verbose(1, "User ${CALLERID(num)} dialed the IVR.")
+ same = n,Goto(Main-IVR,2565551100,1)
+
+;Extension to enter a conference intended only for employees
+exten = 6000,1,Verbose(1, "User ${CALLERID(num)} dialed the employee conference.")
+ same = n,Confbridge(employees)
+ same = n,Hangup()
+
+;Extension to enter a conference intended for employees and customers
+exten = 6500,1,Verbose(1, "User ${CALLERID(num)} dialed the employee/customer mixed conference.")
+ same = n,Confbridge(mixed)
+ same = n,Hangup()
+
+[External-Features]
+; Extension for users to remotely check voicemail. Here we require the caller to
+; enter their mailbox and pincode.
+exten = 2565551234,1,Verbose(1, "User ${CALLERID(num)} dialed into remote voicemail.")
+ same = n,VoiceMailMain(example)
+ same = n,Hangup()
+
+; Extension to queue for sales.
+; The queue has a 300 second timeout.
+exten = 2565551200,1,Verbose(1, "User ${CALLERID(num)} dialed the sales queue.")
+ same = n,Answer()
+ same = n,Queue(sales,,,,300)
+ same = n,Goto(operator,1)
+
+; Extension to queue for a customer advocate.
+; The queue has a 1200 second timeout.
+exten = 2565551250,1,Verbose(1, "User ${CALLERID(num)} dialed the customer advocate queue.")
+ same = n,Answer()
+ same = n,Queue(customer_advocate,,,,1200)
+ same = n,Goto(operator,1)
+
+[Dialing-Errors]
+; Handle any extensions dialed internally that don't otherwise exist.
+; Comment out or remove this extension if you would rather have the calls
+; ignored.
+exten = _X.,1,Verbose(1, "User ${CALLERID(num)} dialed an invalid number.")
+ same = n,Playback(pbx-invalid)
+ same = n,Hangup()
+
+[Internal-Setup]
+; Here we capture internal calls to do anything we need to do before sending
+; them onto all the possible internal locations. Such as disabling CDR on
+; internal to internal calls.
+exten = _X.,1,NoOp()
+ same = n,Set(CDR_PROP(disable)=1)
+ same = n,Goto(Internal-Main,${EXTEN},1)
+
+; The Internal-Main context provides a way for internal callers to get access to most
+; features and functions configured for them. External callers may be sent
+; directly to some of the contexts you see included here, so the included
+; contexts are not necessarily internal only.
+[Internal-Main]
+; The order of includes here is important for matching the right extensions.
+include = Hints
+include = Features
+include = Dial-Users
+include = Dialing-Errors
+
+; Dial-Users handles calls to internal extensions.
+; Calls coming into this context may be *external* or *internal* in origin.
+[Dial-Users]
+exten = _11XX,1,Verbose(1, "User ${CALLERID(num)} dialed ${EXTEN}.")
+ same = n,Set(SAC_DIALED_EXTEN=${EXTEN})
+ same = n,Gotoif($[${DEVICE_STATE(PJSIP/${EXTEN})} = BUSY]?dialed-BUSY,1:)
+ same = n,Dial(PJSIP/${EXTEN}${INTERNAL_DIAL_OPT})
+ same = n,Goto(dialed-${DIALSTATUS},1)
+
+exten = dialed-NOANSWER,1,NoOp()
+ same = n,Voicemail(${SAC_DIALED_EXTEN}@example,u)
+ same = n,Hangup()
+
+exten = dialed-BUSY,1,NoOp()
+ same = n,Voicemail(${SAC_DIALED_EXTEN}@example,b)
+ same = n,Hangup()
+
+exten = dialed-CHANUNAVAIL,1,NoOp()
+ same = n,Playback(pbx-invalid)
+ same = n,Hangup()
+
+exten = _dialed-.,1,Goto(dialed-NOANSWER,1)
+
+exten = h,1,Hangup()
+
+; Callers in the directory may dial 0 which will jump to the
+; 'o' extension.
+exten = o,1,Goto(1111)
+
+; Outbound-Dial
+;
+; Before we dial, see if the extension matches our restricted number patterns.
+; Note that this is a basic set of numbers which could incur a fee if dialed.
+; The NANP includes many other numbers that you may want to block. If you feel
+; it is necessary to block further number patterns you'll have to add them below
+; or you may consider implementing a blacklist via methods beyond the scope of
+; this example.
+[Outbound-Dial]
+exten = _011.,1,Hangup()
+exten = _900NXXXXXX,1,Hangup()
+exten = _1900NXXXXXX,1,Hangup()
+exten = _976XXXX,1,Hangup()
+exten = _NXX976XXXX,1,Hangup()
+exten = _1NXX976XXXX,1,Hangup()
+; Dial outbound through our SIP ITSP.
+exten = _X.,1,Verbose(1, "Didn't match any restricted numbers, proceeding with outbound dial.")
+ same = n,Set(CALLERID(num)=256555${CALLERID(num)})
+ same = n,Dial(PJSIP/${EXTEN}@dcs-endpoint)
+ same = n,Hangup()
+
+; Calls from internal endpoints will enter into one of the two following
+; contexts based on their dialing privilege.
+[Local]
+include = Internal-Setup
+
+exten = _NXXXXXX,1,Goto(Outbound-Dial,1256${EXTEN},1)
+exten = _256NXXXXXX,1,Goto(Outbound-Dial,1${EXTEN},1)
+exten = _1256NXXXXXX,1,Goto(Outbound-Dial,${EXTEN},1)
+
+[Long-Distance]
+include = Local
+
+exten = _NXXNXXXXXX,1,Goto(Outbound-Dial,1${EXTEN},1)
+exten = _1NXXNXXXXXX,1,Goto(Outbound-Dial,${EXTEN},1)
+
+; The DID-Extensions context captures inbound calls from our ITSP that have a
+; DID number where the last four digits matches an internal extension.
+[DID-Extensions]
+exten = _25655511XX,1,Verbose(1, "External caller dialed inbound to DID ${EXTEN})")
+ same = n,Goto(Dial-Users,${EXTEN:6},1)
+
+; Our main IVR program for receiving inbound callers.
+; The IVR script reads “Thank you for calling Super Awesome Company, Waldo’s
+; premier provider of perfect products. If you know your party’s extension,
+; you may dial it at any time. To establish a sales partnership, press one. To
+; speak with a customer advocate, press two. For accounting and other
+; receivables, press three. For a company directory, press four. For an
+; operator, press zero.”
+; demo-congrats is currently used as a placeholder.
+[Main-IVR]
+exten = 2565551100,1,Verbose(1, "New caller, ${CALLERID(num)} dialed into the IVR.")
+ same = n,Answer()
+ same = n(start),Background(basic-pbx-ivr-main)
+ same = n,WaitExten(10)
+ same = n,Background(basic-pbx-ivr-main)
+ same = n,Hangup()
+
+exten = 0,1,Verbose(1, "Caller ${CALLERID(num)} dialed the operator.")
+ same = n,Goto(Dial-Users,1111,1)
+exten = 1,1,Verbose(1, "Caller ${CALLERID(num)} dialed the Sales queue.")
+ same = n,Goto(External-Features,2565551200,1)
+exten = 2,1,Verbose(1, "Caller ${CALLERID(num)} dialed the Customer Experience queue.")
+ same = n,Goto(External-Features,2565551250,1)
+exten = 3,1,Verbose(1, "Caller ${CALLERID(num)} dialed Accounting and Receivables.")
+ same = n,Goto(Dial-Users,1106,1)
+exten = 4,1,Verbose(1, "Caller ${CALLERID(num)} dialed the directory.")
+ same = n,Directory(example,Dial-Users)
+
+exten = i,1,Playback(option-is-invalid)
+ same = n,Goto(2565551100,start)
+
+exten = t,1,Playback(are-you-still-there)
+ same = n,Goto(2565551100,start)
+
+; Calls from our ITSP SIP account arrive in DCS-Incoming. We should be careful
+; to route calls very explicitly so as to avoid any security issues, such as
+; accidentally giving outbound dial access to inbound callers.
+;
+; Each context includes extension pattern matching to match the inbound DID
+; dialed appropriately.
+[DCS-Incoming]
+include = Main-IVR
+include = DID-Extensions
+include = External-Features
diff --git a/configs/basic-pbx/indications.conf b/configs/basic-pbx/indications.conf
new file mode 100644
index 0000000..115cddd
--- /dev/null
+++ b/configs/basic-pbx/indications.conf
@@ -0,0 +1,19 @@
+[general]
+country = us		; We are in Waldo, Al, USA so the US is our default.
+
+[us]
+description = United States / North America
+ringcadence = 2000,4000
+dial = 350+440
+busy = 480+620/500,0/500
+ring = 440+480/2000,0/4000
+congestion = 480+620/250,0/250
+callwaiting = 440/300,0/10000
+dialrecall = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440
+record = 1400/500,0/15000
+info = !950/330,!1400/330,!1800/330,0
+stutter = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440
+
+; Additional country configurations can be found in the Asterisk source
+; at /configs/samples/indications.conf.sample
+
diff --git a/configs/basic-pbx/logger.conf b/configs/basic-pbx/logger.conf
new file mode 100644
index 0000000..8b0a1c1
--- /dev/null
+++ b/configs/basic-pbx/logger.conf
@@ -0,0 +1,9 @@
+[general]
+
+[logfiles]
+
+console = verbose,notice,warning,error
+
+;messages = notice,warning,error
+;full = verbose,notice,warning,error,debug
+;security = security
diff --git a/configs/basic-pbx/modules.conf b/configs/basic-pbx/modules.conf
new file mode 100644
index 0000000..db727b7
--- /dev/null
+++ b/configs/basic-pbx/modules.conf
@@ -0,0 +1,115 @@
+[modules]
+autoload = no
+
+; This is a minimal module load. We are loading only the modules required for
+; the Asterisk features used in the Super Awesome Company configuration.
+
+; Applications
+
+load = app_bridgewait.so
+load = app_dial.so
+load = app_playback.so
+load = app_stack.so
+load = app_verbose.so
+load = app_voicemail.so
+load = app_directory.so
+load = app_confbridge.so
+load = app_queue.so
+
+; Bridging
+
+load = bridge_builtin_features.so
+load = bridge_builtin_interval_features.so
+load = bridge_holding.so
+load = bridge_native_rtp.so
+load = bridge_simple.so
+load = bridge_softmix.so
+
+; Call Detail Records
+
+load = cdr_custom.so
+
+; Channel Drivers
+
+load = chan_bridge_media.so
+load = chan_pjsip.so
+
+; Codecs
+
+load = codec_gsm.so
+load = codec_resample.so
+load = codec_ulaw.so
+load = codec_g722.so
+
+; Formats
+
+load = format_gsm.so
+load = format_pcm.so
+load = format_wav_gsm.so
+load = format_wav.so
+
+; Functions
+
+load = func_callerid.so
+load = func_cdr.so
+load = func_pjsip_endpoint.so
+load = func_sorcery.so
+load = func_devstate.so
+load = func_strings.so
+
+; Core/PBX
+
+load = pbx_config.so
+load = pbx_functions.so
+
+; Resources
+
+load = res_hep_pjsip.so
+load = res_musiconhold.so
+load = res_pjsip_acl.so
+load = res_pjsip_authenticator_digest.so
+load = res_pjsip_caller_id.so
+load = res_pjsip_dialog_info_body_generator.so
+load = res_pjsip_diversion.so
+load = res_pjsip_dtmf_info.so
+load = res_pjsip_endpoint_identifier_anonymous.so
+load = res_pjsip_endpoint_identifier_ip.so
+load = res_pjsip_endpoint_identifier_user.so
+load = res_pjsip_exten_state.so
+load = res_pjsip_header_funcs.so
+load = res_pjsip_log_forwarder.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
+load = res_pjsip_notify.so
+load = res_pjsip_one_touch_record_info.so
+load = res_pjsip_outbound_authenticator_digest.so
+load = res_pjsip_outbound_publish.so
+load = res_pjsip_outbound_registration.so
+load = res_pjsip_path.so
+load = res_pjsip_phoneprov_provider.so
+load = res_pjsip_pidf_body_generator.so
+load = res_pjsip_pidf_digium_body_supplement.so
+load = res_pjsip_pidf_eyebeam_body_supplement.so
+load = res_pjsip_publish_asterisk.so
+load = res_pjsip_pubsub.so
+load = res_pjsip_refer.so
+load = res_pjsip_registrar_expire.so
+load = res_pjsip_registrar.so
+load = res_pjsip_rfc3326.so
+load = res_pjsip_sdp_rtp.so
+load = res_pjsip_send_to_voicemail.so
+load = res_pjsip_session.so
+load = res_pjsip.so
+load = res_pjsip_t38.so
+load = res_pjsip_transport_websocket.so
+load = res_pjsip_xpidf_body_generator.so
+load = res_rtp_asterisk.so
+load = res_sorcery_astdb.so
+load = res_sorcery_config.so
+load = res_sorcery_memory.so
+load = res_sorcery_realtime.so
+load = res_timing_timerfd.so
diff --git a/configs/basic-pbx/musiconhold.conf b/configs/basic-pbx/musiconhold.conf
new file mode 100644
index 0000000..bc3ba21
--- /dev/null
+++ b/configs/basic-pbx/musiconhold.conf
@@ -0,0 +1,5 @@
+[general]
+
+[default]
+mode = files
+directory = moh
diff --git a/configs/basic-pbx/pjsip.conf b/configs/basic-pbx/pjsip.conf
new file mode 100644
index 0000000..00e386a
--- /dev/null
+++ b/configs/basic-pbx/pjsip.conf
@@ -0,0 +1,332 @@
+;================================ TRANSPORTS ==
+; Our primary transport definition for UDP communication behind NAT.
+[transport-udp-nat]
+type = transport
+protocol = udp
+bind = 0.0.0.0
+; NAT settings
+;local_net = 10.0.0.0/8
+;external_media_address = 203.0.113.1
+;external_signaling_address = 203.0.113.1
+
+;================================ CONFIG FOR SIP ITSP ==
+
+; Registration for Digium Cloud Services Account
+
+[dcs-trunk]
+type = registration
+transport = transport-udp-nat
+outbound_auth = dcs-trunk-auth
+server_uri = sip:sip.digiumcloud.net
+;client_uri = sip:myaccountID at sip.digiumcloud.net
+retry_interval = 60
+
+[dcs-trunk-auth]
+type = auth
+auth_type = userpass
+;username = myaccountID
+;password = ASTRONGPASSWORD
+
+; Endpoint for Digium Cloud Services account
+
+[dcs-endpoint]
+type=endpoint
+transport = transport-udp-nat
+context = DCS-Incoming
+allow = !all,g722,ulaw
+outbound_auth = dcs-auth
+aors = dcs-aor
+direct_media = no
+from_domain = sip.digiumcloud.net
+
+[dcs-auth]
+type = auth
+auth_type = userpass
+;username = myaccountID
+;password = ASTRONGPASSWORD
+realm = sip.digiumcloud.net
+
+[dcs-aor]
+type = aor
+contact = sip:sip.digiumcloud.net
+
+[dcs-identify]
+type=identify
+endpoint = dcs-endpoint
+;match = 8.17.32.12
+
+;================================ ENDPOINT TEMPLATES ==
+; Our primary endpoint template for internal desk phones.
+[endpoint-internal-d70](!)
+type = endpoint
+transport = transport-udp-nat
+context = Long-Distance
+allow = !all,g722,ulaw
+direct_media = no
+trust_id_outbound = yes
+device_state_busy_at = 1
+dtmf_mode = rfc4733
+
+[auth-userpass](!)
+type = auth
+auth_type = userpass
+
+[aor-single-reg](!)
+type = aor
+max_contacts = 1
+
+;================================ ENDPOINT DEFINITIONS ==
+; Below are the definitions for all staff devices, listed by department.
+;
+; Super Awesome Company uses the MAC address of their devices for the auth
+; username and the extension number for the name of the endpoint, auth and
+; aor objects. If your phones must use the same user ID and auth name then
+; you will need to customize the endpoints accordingly.
+
+;================================ MANAGEMENT ==
+
+;Lindsey Freddie
+;President for Life
+
+[1107](endpoint-internal-d70)
+auth = 1107
+aors = 1107
+callerid = Lindsey Freddie <1107>
+
+[1107](auth-userpass)
+password = 4webrEtHupHewu4
+username = 0019159BF771
+
+[1107](aor-single-reg)
+mailboxes = 1107 at example
+
+;================================
+;Temple Morgan
+;Life Assistant to the President for Life
+
+[1111](endpoint-internal-d70)
+auth = 1111
+aors = 1111
+callerid = Temple Morgan <1111>
+
+[1111](auth-userpass)
+password = be4eberEkUsUMaF
+username = 000FD3012445
+
+[1111](aor-single-reg)
+mailboxes = 1111 at example
+
+;================================
+;Terry Jules
+;Director of Sales
+
+[1109](endpoint-internal-d70)
+auth = 1109
+aors = 1109
+callerid = Terry Jules <1109>
+
+[1109](auth-userpass)
+password = sPeFaChe7ruxarE
+username = 00094558B29E
+
+[1109](aor-single-reg)
+mailboxes = 1109 at example
+
+;================================
+;Maria Berny
+;Director of Customer Experience
+
+[1101](endpoint-internal-d70)
+auth = 1101
+aors = 1101
+callerid = Maria Berny <1101>
+
+[1101](auth-userpass)
+password = SW2fur7facrarac
+username = 3605657CFB45
+
+[1101](aor-single-reg)
+mailboxes = 1101 at example
+
+;================================
+;Penelope Bronte
+;Director of Engineering
+
+[1103](endpoint-internal-d70)
+auth = 1103
+aors = 1103
+callerid = Penelope Bronte <1103>
+
+[1103](auth-userpass)
+password = zutAnacHe8ewuWr
+username = D5F646797302
+
+[1103](aor-single-reg)
+mailboxes = 103 at example
+
+;================================
+;Aaron Courtney
+;Accounting and Records
+
+[1106](endpoint-internal-d70)
+auth = 1106
+aors = 1106
+callerid = Aaron Courtney <1106>
+
+[1106](auth-userpass)
+password = tecrUBUs3u7uTab
+username = EAFB2F4319C4
+
+[1106](aor-single-reg)
+mailboxes = 1106 at example
+
+;================================ SALES STAFF ==
+
+;================================
+;Garnet Claude
+;Sales Associate
+
+[1105](endpoint-internal-d70)
+auth = 1105
+aors = 1105
+callerid = Garnet Claude <1105>
+
+[1105](auth-userpass)
+password = Q7rAphatRusteSW
+username = 5187E6D311BE
+
+[1105](aor-single-reg)
+mailboxes = 1105 at example
+
+;================================
+;Franny Ocean
+;Sales Associate
+
+[1112](endpoint-internal-d70)
+auth = 1112
+aors = 1112
+callerid = Franny Ocean <1112>
+
+[1112](auth-userpass)
+password = nefReSTAq8phaph
+username = ACC6BC73A990
+
+[1112](aor-single-reg)
+mailboxes = 1112 at example
+
+;================================ CUSTOMER SERVICE STAFF =
+
+;================================
+;Dusty Williams
+;Customer Advocate
+
+[1115](endpoint-internal-d70)
+auth = 1115
+aors = 1115
+callerid = Dusty Williams <1115>
+
+[1115](auth-userpass)
+password = cEBraN2trezaqEt
+username = 2C61DA1AA74B
+
+[1115](aor-single-reg)
+mailboxes = 1115 at example
+
+;================================
+;Tommie Briar
+;Customer Advocate
+
+[1102](endpoint-internal-d70)
+auth = 1102
+aors = 1102
+callerid = Tommie Briar <1102>
+
+[1102](auth-userpass)
+password = 6EBu8egespUwuth
+username = 558EF2645DC7
+
+[1102](aor-single-reg)
+mailboxes = 1102 at example
+
+;================================ ENGINEERING STAFF ==
+
+;================================
+;Hollis Justy
+;Software Engineer
+
+[1110](endpoint-internal-d70)
+auth = 1110
+aors = 1110
+callerid = Hollis Justy <1110>
+
+[1110](auth-userpass)
+password = vust6spuFereThA
+username = D3D55712AED0
+
+[1110](aor-single-reg)
+mailboxes = 1110 at example
+
+;================================
+;Richard Casey
+;Software Engineer
+
+[1104](endpoint-internal-d70)
+auth = 1104
+aors = 1104
+callerid = Richard Casey <1104>
+
+[1104](auth-userpass)
+password = fU8puzuzEpRuSTa
+username = 13B29A457ED5
+
+[1104](aor-single-reg)
+mailboxes = 1104 at example
+
+;================================
+;Sal Smith
+;Software Engineer
+
+[1114](endpoint-internal-d70)
+auth = 1114
+aors = 1114
+callerid = Sal Smith <1114>
+
+[1114](auth-userpass)
+password = XapR4munEcadrub
+username = C369192006EA
+
+[1114](aor-single-reg)
+mailboxes = 1114 at example
+
+;================================
+;Laverne Roberts
+;Software Engineer
+
+[1113](endpoint-internal-d70)
+auth = 1113
+aors = 1113
+callerid = Laverne Roberts <1113>
+
+[1113](auth-userpass)
+password = mu8aPr4daJAQaDE
+username = B07FF579AAC8
+
+[1113](aor-single-reg)
+mailboxes = 1113 at example
+
+;================================
+;Colby Hildred
+;IT Systems
+
+[1108](endpoint-internal-d70)
+auth = 1108
+aors = 1108
+callerid = Colby Hildred <1108>
+
+[1108](auth-userpass)
+password = KAthufrudE6uyAs
+username = DB589C0875AB
+
+[1108](aor-single-reg)
+mailboxes = 1108 at example
+
diff --git a/configs/basic-pbx/queues.conf b/configs/basic-pbx/queues.conf
new file mode 100644
index 0000000..8aaa0b4
--- /dev/null
+++ b/configs/basic-pbx/queues.conf
@@ -0,0 +1,19 @@
+[general]
+monitor-type = MixMonitor
+
+;========================Sales Queue ==
+; Calls all sales persons in a ring-all fashion
+[sales]
+strategy=ringall
+member => PJSIP/1109 ; Terry Jules - Director of Sales
+member => PJSIP/1105 ; Garnet Claude - Sales Associate
+member => PJSIP/1112 ; Franny Ocean - Sales Associate
+
+;===================== Customer Advocate Queue ==
+; Calls all customer advocates in a ring-all fashion
+[customer_advocate]
+strategy=ringall
+member => PJSIP/1101 ; Maria Berny - Director of Customer Experience
+member => PJSIP/1115 ; Dusty Williams - Customer Advocate
+member => PJSIP/1102 ; Tommy Briar - Customer Advocate
+
diff --git a/configs/basic-pbx/voicemail.conf b/configs/basic-pbx/voicemail.conf
new file mode 100644
index 0000000..8ef72d4
--- /dev/null
+++ b/configs/basic-pbx/voicemail.conf
@@ -0,0 +1,23 @@
+[general]
+format = wav49|gsm|wav
+
+[default]
+
+
+[example]
+; Voicemail context for all internal users in the example.com domain.
+1101 = 0717,Maria Berny
+1102 = 7085,Tommie Briar
+1103 = 1809,Penelope Bronte
+1104 = 0039,Richard Casey
+1105 = 6618,Garnet Claude
+1106 = 9805,Aaron Courtney
+1107 = 7484,Lindsey Freddie
+1108 = 7788,Colby Hildred
+1109 = 5750,Terry Jules
+1110 = 3702,Hollis Justy
+1111 = 1878,Temple Morgan
+1112 = 5497,Franny Ocean
+1113 = 1637,Laverne Roberts
+1114 = 3717,Sal Smith
+1115 = 3088,Dusty Williams
diff --git a/configs/samples/amd.conf.sample b/configs/samples/amd.conf.sample
index ce4808a..d7323ec 100644
--- a/configs/samples/amd.conf.sample
+++ b/configs/samples/amd.conf.sample
@@ -15,4 +15,5 @@ between_words_silence = 50	; Minimum duration of silence after a word to conside
 				; the audio what follows as a new word
 maximum_number_of_words = 3	; Maximum number of words in the greeting.
 				; If exceeded then MACHINE
+maximum_word_length = 5000      ; Maximum duration of a single Voice utterance allowed.
 silence_threshold = 256
diff --git a/configs/samples/cdr.conf.sample b/configs/samples/cdr.conf.sample
index 2c7cdf6..e175a2a 100644
--- a/configs/samples/cdr.conf.sample
+++ b/configs/samples/cdr.conf.sample
@@ -154,6 +154,8 @@ usegmtime=yes    ; log date/time in GMT.  Default is "no"
 loguniqueid=yes  ; log uniqueid.  Default is "no"
 loguserfield=yes ; log user field.  Default is "no"
 accountlogs=yes  ; create separate log file for each account code. Default is "yes"
+;newcdrcolumns=yes ; Enable logging of post-1.8 CDR columns (peeraccount, linkedid, sequence).
+                   ; Default is "no".
 
 ;[radius]
 ;usegmtime=yes    ; log date/time in GMT
diff --git a/configs/samples/cdr_odbc.conf.sample b/configs/samples/cdr_odbc.conf.sample
index 93bd6ff..663ce09 100644
--- a/configs/samples/cdr_odbc.conf.sample
+++ b/configs/samples/cdr_odbc.conf.sample
@@ -9,3 +9,4 @@
 ;table=cdr		;"cdr" is default table name
 ;usegmtime=no             ; set to "yes" to log in GMT
 ;hrtime=yes		;Enables microsecond accuracy with the billsec and duration fields
+;newcdrcolumns=yes ; Enable logging of post-1.8 CDR columns (peeraccount, linkedid, sequence)
diff --git a/configs/samples/chan_dahdi.conf.sample b/configs/samples/chan_dahdi.conf.sample
index 13691fc..a5daede 100644
--- a/configs/samples/chan_dahdi.conf.sample
+++ b/configs/samples/chan_dahdi.conf.sample
@@ -196,6 +196,22 @@ context=public
 ;
 ;resetinterval = 3600
 ;
+; Enable per ISDN span to force a RESTART on a channel that returns a cause
+; code of PRI_CAUSE_REQUESTED_CHAN_UNAVAIL(44).  If this option is enabled
+; and the reason the peer rejected the call with cause 44 was that the
+; channel is stuck in an unavailable state on the peer, then this might
+; help release the channel.  It is worth noting that the next outgoing call
+; Asterisk makes will likely try the same channel again.
+;
+; NOTE: Sending a RESTART in response to a cause 44 is not required
+; (nor prohibited) by the standards and is likely a primitive chan_dahdi
+; response to call collisions (glare) and buggy peers.  However, there
+; are telco switches out there that ignore the RESTART and continue to
+; send calls to the channel in the restarting state.
+; Default yes in current release branches for backward compatibility.
+;
+;force_restart_unavailable_chans=yes
+;
 ; Assume inband audio may be present when a SETUP ACK message is received.
 ; Q.931 Section 5.1.3 says that in scenarios with overlap dialing, when a
 ; dialtone is sent from the network side, progress indicator 8 "Inband info
diff --git a/configs/samples/confbridge.conf.sample b/configs/samples/confbridge.conf.sample
index 860f1cb..0419001 100644
--- a/configs/samples/confbridge.conf.sample
+++ b/configs/samples/confbridge.conf.sample
@@ -137,6 +137,12 @@ type=user
                        ; This option is off by default.
 ;announcement=</path/to/file> ; Play a sound file to the user when they join the conference.
 
+;timeout=3600 ; When set non-zero, this specifies the number of seconds that the participant
+              ; may stay in the conference before being automatically ejected. When the user
+              ; is ejected from the conference, the user's channel will have the CONFBRIDGE_RESULT
+              ; variable set to "TIMEOUT". A value of 0 indicates that there is no timeout.
+              ; Default: 0
+
 ; --- ConfBridge Bridge Profile Options ---
 [default_bridge]
 type=bridge
diff --git a/configs/samples/extensions_minivm.conf.sample b/configs/samples/extensions_minivm.conf.sample
index 832e1d2..2f9d246 100644
--- a/configs/samples/extensions_minivm.conf.sample
+++ b/configs/samples/extensions_minivm.conf.sample
@@ -1,6 +1,6 @@
 ; MINI-VOICEMAIL dialplan example
 ; ---------------------------------------------------------------------------------------
-; ASTERISK_FILE_VERSION(__FILE__, "$Revision: 418870 $")
+; ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 ;
 ;
 ; This is an example on how to use the Mini-Voicemail system to build
diff --git a/configs/samples/features.conf.sample b/configs/samples/features.conf.sample
index 56b334f..aeb25c0 100644
--- a/configs/samples/features.conf.sample
+++ b/configs/samples/features.conf.sample
@@ -28,7 +28,10 @@
                                 ; being kicked back to the original call.
 ;transferretrysound = "beep"    ; Sound to play when a transferer fails to dial a valid extension.
 ;transferinvalidsound = "beeperr" ; Sound to play when a transferer fails to dial a valid extension and is out of retries.
-
+;atxferabort = *1               ; cancel the attended transfer
+;atxfercomplete = *2            ; complete the attended transfer, dropping out of the call
+;atxferthreeway = *3            ; complete the attended transfer, but stay in the call. This will turn the call into a multi-party bridge
+;atxferswap = *4                ; swap to the other party. Once an attended transfer has begun, this options may be used multiple times
 
 ; Note that the DTMF features listed below only work when two channels have answered and are bridged together.
 ; They can not be used while the remote party is ringing or in progress. If you require this feature you can use
diff --git a/configs/samples/http.conf.sample b/configs/samples/http.conf.sample
index 44095a1..342dff4 100644
--- a/configs/samples/http.conf.sample
+++ b/configs/samples/http.conf.sample
@@ -13,6 +13,16 @@
 ;
 [general]
 ;
+; The name of the server, advertised in both the Server field in HTTP
+; response message headers, as well as the <address /> element in certain HTTP
+; response message bodies. If not furnished here, "Asterisk/{version}" will be
+; used as a default value for the Server header field and the <address />
+; element. Setting this property to a blank value will result in the omission
+; of the Server header field from HTTP response message headers and the
+; <address /> element from HTTP response message bodies.
+;
+;servername=Asterisk
+;
 ; Whether HTTP/HTTPS interface is enabled or not.  Default is no.
 ; This also affects manager/rawman/mxml access (see manager.conf)
 ;
@@ -80,6 +90,26 @@ bindaddr=127.0.0.1
 ; private in same .pem file.
 ; openssl req -new -x509 -days 365 -nodes -out /tmp/foo.pem -keyout /tmp/foo.pem
 ;
+; tlscipher=                             ; The list of allowed ciphers
+;                                        ; if none are specified the following cipher
+;                                        ; list will be used instead:
+; ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:
+; ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:
+; kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:
+; ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:
+; ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:
+; DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:
+; AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:
+; AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:
+; !EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA
+;
+; tlsdisablev1=yes                ; Disable TLSv1 support - if not set this defaults to "yes"
+; tlsdisablev11=yes               ; Disable TLSv1.1 support - if not set this defaults to "no"
+; tlsdisablev12=yes               ; Disable TLSv1.2 support - if not set this defaults to "no"
+;
+; tlsservercipherorder=yes        ; Use the server preference order instead of the client order
+;                                 ; Defaults to "yes"
+;
 ; The post_mappings section maps URLs to real paths on the filesystem.  If a
 ; POST is done from within an authenticated manager session to one of the
 ; configured POST mappings, then any files in the POST will be placed in the
diff --git a/configs/samples/iax.conf.sample b/configs/samples/iax.conf.sample
index e17c7df..1d9623a 100644
--- a/configs/samples/iax.conf.sample
+++ b/configs/samples/iax.conf.sample
@@ -170,12 +170,6 @@ disallow=lpc10
 ; jitterbuffer=yes|no: global default as to whether you want
 ; the jitter buffer at all.
 ;
-; forcejitterbuffer=yes|no: in the ideal world, when we bridge VoIP channels
-; we don't want to do jitterbuffering on the switch, since the endpoints
-; can each handle this.  However, some endpoints may have poor jitterbuffers
-; themselves, so this option will force * to always jitterbuffer, even in this
-; case.
-;
 ; maxjitterbuffer: a maximum size for the jitter buffer.
 ; Setting a reasonable maximum here will prevent the call delay
 ; from rising to silly values in extreme situations; you'll hear
@@ -202,7 +196,6 @@ disallow=lpc10
 ;
 
 jitterbuffer=no
-forcejitterbuffer=no
 ;maxjitterbuffer=1000
 ;maxjitterinterps=10
 ;resyncthreshold=1000
diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample
index a9b2d2b..859635c 100644
--- a/configs/samples/pjsip.conf.sample
+++ b/configs/samples/pjsip.conf.sample
@@ -167,6 +167,11 @@
 ; "contact_user=" sets the SIP contact header's user portion of the SIP URI
 ; this will affect the extension reached in dialplan when the far end calls you at this
 ; registration. The default is 's'.
+;
+; If you would like to enable line support and have incoming calls related to this
+; registration go to an endpoint automatically the "line" and "endpoint" options must
+; be set. The "endpoint" option specifies what endpoint the incoming call should be
+; associated with.
 
 ;[mytrunk]
 ;type=registration
@@ -178,6 +183,8 @@
 ;retry_interval=60
 ;forbidden_retry_interval=600
 ;expiration=3600
+;line=yes
+;endpoint=mytrunk
 
 ;[mytrunk_auth]
 ;type=auth
@@ -628,6 +635,7 @@
                         ; information to the called user agent (default: "yes")
 ;send_pai=no    ; Send the P Asserted Identity header (default: "no")
 ;send_rpid=no   ; Send the Remote Party ID header (default: "no")
+;rpid_immediate=no      ; Send connected line updates on unanswered incoming calls immediately. (default: "no")
 ;timers_min_se=90       ; Minimum session timers expiration period (default:
                         ; "90")
 ;timers=yes     ; Session timers for SIP packets (default: "yes")
@@ -648,6 +656,11 @@
                         ; "no")
 ;media_encryption_optimistic=no ; Use encryption if possible but don't fail the call
 								; if not possible.
+;g726_non_standard=no   ; When set to "yes" and an endpoint negotiates g.726
+                        ; audio then g.726 for AAL2 packing order is used contrary
+                        ; to what is recommended in RFC3551. Note, 'g726aal2' also
+                        ; needs to be specified in the codec allow list
+                        ; (default: "no")
 ;inband_progress=no     ; Determines whether chan_pjsip will indicate ringing
                         ; using inband progress (default: "no")
 ;call_group=    ; The numeric pickup groups for a channel (default: "")
@@ -719,6 +732,15 @@
                 ; byte tags (default: "no")
 ;set_var=       ; Variable set on a channel involving the endpoint. For multiple
 		; channel variables specify multiple 'set_var'(s)
+;rtp_keepalive= ; Interval, in seconds, between comfort noise RTP packets if
+                ; RTP is not flowing. This setting is useful for ensuring that
+                ; holes in NATs and firewalls are kept open throughout a call.
+;rtp_timeout=      ; Hang up channel if RTP is not received for the specified
+                   ; number of seconds when the channel is off hold (default:
+                   ; "0" or not enabled)
+;rtp_timeout_hold= ; Hang up channel if RTP is not received for the specified
+                   ; number of seconds when the channel is on hold (default:
+                   ; "0" or not enabled)
 
 ;==========================AUTH SECTION OPTIONS=========================
 ;[auth]
@@ -749,6 +771,10 @@
         ; "")
 ;ca_list_file=  ; File containing a list of certificates to read TLS ONLY
                 ; (default: "")
+;ca_list_path=  ; Path to directory containing certificates to read TLS ONLY.
+                ; PJProject version 2.4 or higher is required for this option to
+				; be used.
+                ; (default: "")
 ;cert_file=     ; Certificate file for endpoint TLS ONLY
                 ; Will read .crt or .pem file but only uses cert,
                 ; a .key file must be specified via priv_key_file
@@ -782,17 +808,6 @@
                                 ; information. Value is in milliseconds; default
                                 ; is 100 ms.
 
-;==========================CONTACT SECTION OPTIONS=========================
-;[contact]
-;  SYNOPSIS: A way of creating an aliased name to a SIP URI
-;type=  ; Must be of type contact (default: "")
-;uri=   ; SIP URI to contact peer (default: "")
-;expiration_time=       ; Time to keep alive a contact (default: "")
-;qualify_frequency=0    ; Interval at which to qualify a contact (default: "0")
-;outbound_proxy=        ; Outbound proxy used when sending OPTIONS request
-                        ; (default: "")
-
-
 ;==========================AOR SECTION OPTIONS=========================
 ;[aor]
 ;  SYNOPSIS: The configuration for a location of an endpoint
@@ -809,6 +824,7 @@
                         ; (default: "no")
 ;type=  ; Must be of type aor (default: "")
 ;qualify_frequency=0    ; Interval at which to qualify an AoR (default: "0")
+;qualify_timeout=3.0      ; Qualify timeout in fractional seconds (default: "3.0")
 ;authenticate_qualify=no        ; Authenticates a qualify request if needed
                                 ; (default: "no")
 ;outbound_proxy=        ; Outbound proxy used when sending OPTIONS request
@@ -858,7 +874,17 @@
                                                         ; int")
 ;debug=no ; Enable/Disable SIP debug logging.  Valid options include yes|no
           ; or a host address (default: "no")
-
+;keep_alive_interval=20 ; The interval (in seconds) at which to send keepalive
+                        ; messages on all active connection-oriented transports
+                        ; (default: "0")
+;endpoint_identifier_order=ip,username,anonymous
+            ; The order by which endpoint identifiers are given priority.
+            ; Identifier names are derived from res_pjsip_endpoint_identifier_*
+            ; modules. (default: ip,username,anonymous)
+;max_initial_qualify_time=4 ; The maximum amount of time (in seconds) from
+                            ; startup that qualifies should be attempted on all
+                            ; contacts.  If greater than the qualify_frequency
+                            ; for an aor, qualify_frequency will be used instead.
 
 ; MODULE PROVIDING BELOW SECTION(S): res_pjsip_acl
 ;==========================ACL SECTION OPTIONS=========================
@@ -897,6 +923,14 @@
                         ; registration is unsuccessful (default: "60")
 ;forbidden_retry_interval=0     ; Interval used when receiving a 403 Forbidden
                                 ; response (default: "0")
+;fatal_retry_interval=0 ; Interval used when receiving a fatal response.
+                        ; (default: "0") A fatal response is any permanent
+                        ; failure (non-temporary 4xx, 5xx, 6xx) response
+                        ; received from the registrar. NOTE - if also set
+                        ; the 'forbidden_retry_interval' takes precedence
+                        ; over this one when a 403 is received. Also, if
+                        ; 'auth_rejection_permanent' equals 'yes' a 401 and
+                        ; 407 become subject to this retry interval.
 ;server_uri=    ; SIP URI of the server to register against (default: "")
 ;transport=     ; Transport used for outbound authentication (default: "")
 ;type=  ; Must be of type registration (default: "")
diff --git a/configs/samples/pjsip_wizard.conf.sample b/configs/samples/pjsip_wizard.conf.sample
new file mode 100644
index 0000000..0f46083
--- /dev/null
+++ b/configs/samples/pjsip_wizard.conf.sample
@@ -0,0 +1,147 @@
+; PJSIP Wizard Configuration Samples and Quick Reference
+;
+; This file has several very basic configuration examples, to serve as a quick
+; reference to jog your memory when you need to write up a new configuration.
+; It is not intended to teach PJSIP configuration or serve as an exhaustive
+; reference of options and potential scenarios.
+;
+; This file has two main sections.
+; First, manually written examples to serve as a handy reference.
+; Second, a list of all possible PJSIP config options by section. This is
+; pulled from the XML config help. It only shows the synopsis for every item.
+; If you want to see more detail please check the documentation sources
+; mentioned at the top of this file.
+
+; Documentation
+;
+; The official documentation is at http://wiki.asterisk.org
+; You can read the XML configuration help via Asterisk command line with
+; "config show help res_pjsip_config_wizard", then you can drill down through
+; the various sections and their options.
+;
+
+
+;============EXAMPLE WIZARD CONFIGURATION FOR A PHONE=======================
+
+; This config would create an endpoint, aor with dynamic contact, inbound
+; auth, a phoneprov object and a dialplan hint for extension 1000.
+
+;[myphone]
+;type = wizard
+;accepts_auth = yes
+;accepts_registrations = yes
+;has_phoneprov = yes
+;transport = ipv4
+;has_hint = yes
+;hint_exten = 1000
+;inbound_auth/username = testname
+;inbound_auth/password = test password
+;endpoint/allow = ulaw
+;endpoint/context = default
+;phoneprov/MAC = 001122aa4455
+;phoneprov/PROFILE = profile1
+
+
+;============EXAMPLE WIZARD CONFIGURATION FOR AN ITSP TRUNK=================
+
+; This ITSP has 2 servers available and requires registration.
+
+; This config would create an endpoint, an aor with 2 static contacts, an
+; outbound auth, an identify with 2 matches, and 2 registrations.
+
+;[mytrunk]
+;type = wizard
+;sends_auth = yes
+;sends_registrations = yes
+;transport = ipv4
+;remote_hosts = sip1.myitsp.com:5060,sip2.myitsp.com:5060
+;outbound_auth/username = testname
+;outbound_auth/password = test password
+;endpoint/allow = ulaw
+;endpoint/context = default
+
+
+;========================WIZARD SECTION OPTIONS===============================
+;[wizard]
+;  SYNOPSIS: Provides configuration wizard for common scenarios.
+;sends_auth=    ; Will create an outbound auth object for the endpoint and
+                ; registration.
+                ; If yes, outbound/username must be specified.
+                ; (default = "no")
+
+;accepts_auth=  ; Will create an inbound auth object for the endpoint.
+                ; If yes, inbound/username must be specified.
+                ; (default = "no")
+
+;sends_registrations=    ; Will create an outbound registration object and an
+                         ; identify match for each host in remote_hosts (which
+                         ; must be specified).
+                         ; sends_auth must also be specified.
+                         ; (default: "no")
+
+;accepts_registrations=  ; Will create an aor with dynamic contacts which will
+                         ; accept registrations.
+                         ; accepts_auth must also be specified.
+                         ; (default: "no")
+
+;remote_hosts=   ; A comma separated list of remote hosts in the form of
+                 ; <ipaddress | hostname>[:port] [,<ipaddress | hostname>[:port] ] ...
+                 ; If specified, a static contact for each host will be created
+                 ; in the aor.  If accepts_registrations is no, an identify
+                 ; object is also created with a match line for each remote host.
+                 ; If an aor/contact or match/identify is explicitly supplied,
+                 ; remote_hosts will not be used to automatically create contacts
+                 ; or matches respectively.
+                 ; Hostnames must resolve to A, AAAA or CNAME records.
+                 ; SRV records are not currently supported.
+                 ; (default: "")
+
+;transport=      ; The transport to use for the endpoint and registrations
+                 ; (default: the pjsip default)
+
+;server_uri_pattern= ; The pattern used to construct the registration
+                     ; server_uri. The replaceable parameter ${REMOTE_HOST} isa
+                     ; available for use.
+                     ; (default: "sip:${REMOTE_HOST}")
+
+;client_uri_pattern= ; The pattern used to construct the registration client_uri.
+                     ; The replaceable parameters ${REMOTE_HOST} and ${USERNAME}
+                     ; are available for use.
+                     ; (default: "sip:${USERNAME}@${REMOTE_HOST}")
+
+;contact_pattern=    ; The pattern used to construct the aor contact.
+                     ; The replaceable parameter ${REMOTE_HOST} is available
+                     ; for use.
+                     ; (default: "sip:${REMOTE_HOST}")
+
+;has_phoneprov=      ; Will create a phoneprov object.
+                     ; If yes, phoneprov/MAC must be specified.
+                     ; (default: "no")
+
+;has_hint=           ; Create hint and optionally a default application.
+                     ; (default: "no")
+
+;hint_context        ; Any hints created for this wizard will be placed in this
+                     ; context.
+                     ; (default: endpoint/context)
+
+;hint_exten          ; If specified, a PJSIP/<wizard_id> hint will be created
+                     ; for this extension in 'hint_context'.
+                     ; context.
+                     ; (default: none)
+
+;hint_application    ; If specified, an extension will be placed in 'hint_context'
+                     ; at priority 1 that calls this application.  Could be any
+                     ; valid dialplan expression like
+                     ; "Gosub(stdexten,${EXTEN},1(${HINT}))"
+                     ; (default: "Dial(${HINT})")
+
+;endpoint/<param>      ; Any parameters to be passed directly to and validated
+;aor/<param>           ; by their respective objects.
+;inbound_auth/<param>
+;outbound_auth/<param>
+;identify/<param>
+;registration/<param>
+;phoneprov/<param>
+
+;type=          ; Must be of type wizard (default: "")
diff --git a/configs/samples/queues.conf.sample b/configs/samples/queues.conf.sample
index bea62e8..85cf9e4 100644
--- a/configs/samples/queues.conf.sample
+++ b/configs/samples/queues.conf.sample
@@ -224,8 +224,13 @@ monitor-type = MixMonitor
 ;
 ;maxlen = 0
 ;
+; Note: for below queue channel options (setinterfacevar, setqueueentryvar,
+; setqueuevar) if the caller channel is a local channel and optimizations
+; is enabled then after optimization has occurred only the queue member
+; channel will contain the variables.
+;
 ; If set to yes, just prior to the caller being bridged with a queue member
-; the following variables will be set
+; the following variables will be set on the caller and queue member channels:
 ; MEMBERINTERFACE is the interface name (eg. Agent/1234)
 ; MEMBERNAME is the member name (eg. Joe Soap)
 ; MEMBERCALLS is the number of calls that interface has taken,
@@ -237,15 +242,16 @@ monitor-type = MixMonitor
 ;setinterfacevar=no
 ;
 ; If set to yes, just prior to the caller being bridged with a queue member
-; the following variables will be set:
+; the following variables will be set on the caller and queue member channels:
 ; QEHOLDTIME callers hold time
 ; QEORIGINALPOS original position of the caller in the queue
 ;
 ;setqueueentryvar=no
 ;
 ; If set to yes, the following variables will be set
-; just prior to the caller being bridged with a queue member
-; and just prior to the caller leaving the queue
+; just prior to the caller being bridged with a queue member (set on the
+; caller and queue member channels) and just prior to the caller
+; leaving the queue
 ; QUEUENAME name of the queue
 ; QUEUEMAX maxmimum number of calls allowed
 ; QUEUESTRATEGY the strategy of the queue;
diff --git a/configs/samples/res_fax.conf.sample b/configs/samples/res_fax.conf.sample
index dfaa4ce..022a23a 100644
--- a/configs/samples/res_fax.conf.sample
+++ b/configs/samples/res_fax.conf.sample
@@ -26,3 +26,7 @@ statusevents=yes
 ; Enable/disable T.30 ECM (error correction mode) by default.
 ; Default: Enabled
 ;ecm=yes
+
+; T.38 Negotiation Timeout in milliseconds
+; Default: 5000
+t38timeout=5000
diff --git a/configs/samples/sip.conf.sample b/configs/samples/sip.conf.sample
index a438f46..a24ab30 100644
--- a/configs/samples/sip.conf.sample
+++ b/configs/samples/sip.conf.sample
@@ -229,6 +229,10 @@ tcpbindaddr=0.0.0.0             ; IP address for TCP server to bind to (0.0.0.0
 				; unauthenticated sessions that will be allowed
                                 ; to connect at any given time. (default: 100)
 
+;websocket_enabled = true       ; Set to false to prevent chan_sip from listening to websockets.  This
+                                ; is neeeded when using chan_sip and res_pjsip_transport_websockets on
+                                ; the same system.
+
 ;websocket_write_timeout = 100  ; Default write timeout to set on websocket transports.
                                 ; This value may need to be adjusted for connections where
                                 ; Asterisk must write a substantial amount of data and the
@@ -379,10 +383,10 @@ srvlookup=yes                   ; Enable DNS SRV lookups on outbound calls
                                 ; In order for "noanswer" applications to work, you need to run
                                 ; the progress() application in the priority before the app.
 
-;progressinband=never           ; If we should generate in-band ringing always
+;progressinband=no              ; If we should generate in-band ringing. Always
                                 ; use 'never' to never use in-band signalling, even in cases
                                 ; where some buggy devices might not render it
-                                ; Valid values: yes, no, never Default: never
+                                ; Valid values: yes, no, never Default: no
 ;useragent=Asterisk PBX         ; Allows you to change the user agent string
                                 ; The default user agent string also contains the Asterisk
                                 ; version. If you don't want to expose this, change the
@@ -802,7 +806,7 @@ srvlookup=yes                   ; Enable DNS SRV lookups on outbound calls
 ;
 ;register => tls://username:xxxxxx@sip-tls-proxy.example.org
 ;
-;    The 'transport' part defaults to 'udp' but may also be 'tcp', 'tls', 'ws', or 'wss'.
+;    The 'transport' part defaults to 'udp' but may also be 'tcp' or 'tls'.
 ;    Using 'udp://' explicitly is also useful in case the username part
 ;    contains a '/' ('user/name').
 
diff --git a/configure b/configure
index 823e3cb..8c3abe9 100755
--- a/configure
+++ b/configure
@@ -1,5 +1,5 @@
 #! /bin/sh
-# From configure.ac Revision: 425965 .
+# From configure.ac Revision.
 # Guess values for system-dependent variables and create Makefiles.
 # Generated by GNU Autoconf 2.69 for asterisk trunk.
 #
@@ -651,6 +651,8 @@ PBX_MSG_NOSIGNAL
 PBX_IXJUSER
 GMIME_LIBS
 GMIME_CFLAGS
+PBX_SSL_OP_NO_TLSV1_2
+PBX_SSL_OP_NO_TLSV1_1
 OPENH323_BUILD
 OPENH323_SUFFIX
 OPENH323_LIBDIR
@@ -687,7 +689,6 @@ PBX_RTLD_NOLOAD
 PBX_GLOB_BRACE
 PBX_GLOB_NOMAGIC
 AST_RPATH
-AST_NESTED_FUNCTIONS
 AST_NATIVE_ARCH
 AST_SHADOW_WARNINGS
 AST_NO_STRICT_OVERFLOW
@@ -902,6 +903,14 @@ PBX_PORTAUDIO
 PORTAUDIO_DIR
 PORTAUDIO_INCLUDE
 PORTAUDIO_LIB
+PBX_PJ_SSL_CERT_LOAD_FROM_FILES2
+PJ_SSL_CERT_LOAD_FROM_FILES2_DIR
+PJ_SSL_CERT_LOAD_FROM_FILES2_INCLUDE
+PJ_SSL_CERT_LOAD_FROM_FILES2_LIB
+PBX_PJSIP_GET_DEST_INFO
+PJSIP_GET_DEST_INFO_DIR
+PJSIP_GET_DEST_INFO_INCLUDE
+PJSIP_GET_DEST_INFO_LIB
 PBX_PJSIP_REPLACE_MEDIA_STREAM
 PJSIP_REPLACE_MEDIA_STREAM_DIR
 PJSIP_REPLACE_MEDIA_STREAM_INCLUDE
@@ -1082,6 +1091,10 @@ PBX_DAHDI
 DAHDI_DIR
 DAHDI_INCLUDE
 DAHDI_LIB
+PBX_OPENSSL_ECDH_AUTO
+OPENSSL_ECDH_AUTO_DIR
+OPENSSL_ECDH_AUTO_INCLUDE
+OPENSSL_ECDH_AUTO_LIB
 PBX_OPENSSL_EC
 OPENSSL_EC_DIR
 OPENSSL_EC_INCLUDE
@@ -1130,6 +1143,10 @@ PBX_ALSA
 ALSA_DIR
 ALSA_INCLUDE
 ALSA_LIB
+AST_C_COMPILER_FAMILY
+AST_CLANG_BLOCKS
+AST_CLANG_BLOCKS_LIBS
+AST_NESTED_FUNCTIONS
 AST_CODE_COVERAGE
 AST_DEVMODE_STRICT
 AST_DEVMODE
@@ -8207,6 +8224,146 @@ fi
 
 
 
+
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for RAII support" >&5
+$as_echo_n "checking for RAII support... " >&6; }
+	AST_C_COMPILER_FAMILY=""
+	cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+			int main() {
+				#if defined(__clang__)
+				choke
+				#endif
+				return 0;
+			}
+
+  ;
+  return 0;
+}
+
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+						{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for gcc -fnested-functions" >&5
+$as_echo_n "checking for gcc -fnested-functions... " >&6; }
+			cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+
+int
+main ()
+{
+auto void foo(void); void foo(void) {}
+  ;
+  return 0;
+}
+
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+					AST_NESTED_FUNCTIONS=""
+					{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+else
+
+					AST_NESTED_FUNCTIONS="-fnested-functions"
+					{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+			AST_C_COMPILER_FAMILY="gcc"
+
+else
+
+			{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for clang -fblocks" >&5
+$as_echo_n "checking for clang -fblocks... " >&6; }
+			if test "`echo "int main(){return ^{return 42;}();}" | ${CC} -o /dev/null -fblocks -x c - 2>&1`" = ""; then
+				AST_CLANG_BLOCKS_LIBS=""
+				AST_CLANG_BLOCKS="-Wno-unknown-warning-option -fblocks"
+				{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+			elif test "`echo "int main(){return ^{return 42;}();}" | ${CC} -o /dev/null -fblocks -x c -lBlocksRuntime - 2>&1`" = ""; then
+				AST_CLANG_BLOCKS_LIBS="-lBlocksRuntime"
+				AST_CLANG_BLOCKS="-fblocks"
+				{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+			else
+				as_fn_error $? "BlocksRuntime is required for clang, please install libblocksruntime" "$LINENO" 5
+			fi
+
+
+			AST_C_COMPILER_FAMILY="clang"
+
+
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+	if test -z "${AST_C_COMPILER_FAMILY}"; then
+		as_fn_error $? "Compiler ${CC} not supported. Mminimum required gcc-4.3 / llvm-gcc-4.3 / clang-3.3 + libblocksruntime-dev" "$LINENO" 5
+	fi
+
+
+
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for clang strsep/strcmp optimization" >&5
+$as_echo_n "checking for clang strsep/strcmp optimization... " >&6; }
+	save_CFLAGS="$CFLAGS"
+	CFLAGS="$CFLAGS -O1 -Werror=array-bounds"
+	cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+
+				#include <stdio.h>
+				#include <string.h>
+
+				/* fails with clang and -O1 */
+				void test_strsep_strcmp (void) {
+					char *haystackstr = "test1,test2";
+					char *outstr;
+					if (!strcmp(haystackstr, ",")) {
+						printf("fail\n");
+					}
+					if ((outstr = strsep(&haystackstr, ","))) {
+						printf("fail:%s\n", outstr);
+					}
+				}
+				int main(int argc, char *argv) {
+					test_strsep_strcmp();
+					return 0;
+				}
+
+
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+			{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+else
+
+
+$as_echo "#define _HAVE_STRING_ARCH_strcmp 1" >>confdefs.h
+
+
+$as_echo "#define _HAVE_STRING_ARCH_strsep 1" >>confdefs.h
+
+			{ $as_echo "$as_me:${as_lineno-$LINENO}: result: prevent use of __string2_1bptr_p / strsep / strcmp from bits/string2.h" >&5
+$as_echo "prevent use of __string2_1bptr_p / strsep / strcmp from bits/string2.h" >&6; }
+
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+	CFLAGS="$save_CFLAGS"
+
+
 # AST_EXT_LIB_SETUP is used to tell configure to handle variables for
 # various packages.
 # $1 is the prefix for the variables in makeopts and autoconfig.h
@@ -8545,6 +8702,18 @@ PBX_OPENSSL_EC=0
 
 
 
+OPENSSL_ECDH_AUTO_DESCRIP="OpenSSL Auto ECDH Support"
+OPENSSL_ECDH_AUTO_OPTION=crypto
+OPENSSL_ECDH_AUTO_DIR=${CRYPTO_DIR}
+
+PBX_OPENSSL_ECDH_AUTO=0
+
+
+
+
+
+
+
     DAHDI_DESCRIP="DAHDI"
     DAHDI_OPTION="dahdi"
     PBX_DAHDI=0
@@ -10270,6 +10439,30 @@ PBX_PJSIP_REPLACE_MEDIA_STREAM=0
 
 
 
+PJSIP_GET_DEST_INFO_DESCRIP="pjsip_get_dest_info support"
+PJSIP_GET_DEST_INFO_OPTION=pjsip
+PJSIP_GET_DEST_INFO_DIR=${PJPROJECT_DIR}
+
+PBX_PJSIP_GET_DEST_INFO=0
+
+
+
+
+
+
+
+PJ_SSL_CERT_LOAD_FROM_FILES2_DESCRIP="pj_ssl_cert_load_from_files2 support"
+PJ_SSL_CERT_LOAD_FROM_FILES2_OPTION=pjsip
+PJ_SSL_CERT_LOAD_FROM_FILES2_DIR=${PJPROJECT_DIR}
+
+PBX_PJ_SSL_CERT_LOAD_FROM_FILES2=0
+
+
+
+
+
+
+
     PORTAUDIO_DESCRIP="PortAudio"
     PORTAUDIO_OPTION="portaudio"
     PBX_PORTAUDIO=0
@@ -15305,7 +15498,7 @@ fi
 done
 
 
-for ac_func in asprintf atexit closefrom dup2 eaccess endpwent euidaccess ffsll ftruncate getcwd gethostbyname gethostname getloadavg gettimeofday glob htonll ioperm inet_ntoa isascii memchr memmove memset mkdir mkdtemp munmap ntohll newlocale ppoll putenv re_comp regcomp select setenv socket strcasecmp strcasestr strchr strcspn strdup strerror strlcat strlcpy strncasecmp strndup strnlen strrchr strsep strspn strstr strtod strtol strtold strtoq unsetenv utime vasprintf getpeereid sysctl swapctl
+for ac_func in asprintf atexit closefrom dup2 eaccess endpwent euidaccess ffsll ftruncate getcwd gethostbyname gethostname getloadavg gettimeofday glob ioperm inet_ntoa isascii memchr memmove memset mkdir mkdtemp munmap newlocale ppoll putenv re_comp regcomp select setenv socket strcasecmp strcasestr strchr strcspn strdup strerror strlcat strlcpy strncasecmp strndup strnlen strrchr strsep strspn strstr strtod strtol strtold strtoq unsetenv utime vasprintf getpeereid sysctl swapctl
 do :
   as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
 ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
@@ -15318,6 +15511,60 @@ fi
 done
 
 
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for htonll" >&5
+$as_echo_n "checking for htonll... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <arpa/inet.h>
+int
+main ()
+{
+return htonll(0);
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+$as_echo "#define HAVE_HTONLL 1" >>confdefs.h
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ntohll" >&5
+$as_echo_n "checking for ntohll... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <arpa/inet.h>
+int
+main ()
+{
+return ntohll(0);
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+$as_echo "#define HAVE_NTOHLL 1" >>confdefs.h
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+
 # NOTE: we use AC_CHECK_LIB to get -lm into the arguments for later checks,
 # so that AC_CHECK_FUNCS can detect functions in that library.
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sqrt in -lm" >&5
@@ -16057,10 +16304,14 @@ rm -f core conftest.err conftest.$ac_objext \
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working unnamed semaphores" >&5
 $as_echo_n "checking for working unnamed semaphores... " >&6; }
 if test "$cross_compiling" = yes; then :
-  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-as_fn_error $? "cannot run test program while cross compiling
-See \`config.log' for more details" "$LINENO" 5; }
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: cross-compile" >&5
+$as_echo "cross-compile" >&6; }
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: result yes guessed because of cross compilation" >&5
+$as_echo "$as_me: WARNING: result yes guessed because of cross compilation" >&6;}
+
+$as_echo "#define HAS_WORKING_SEMAPHORE 1" >>confdefs.h
+
+
 else
   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
@@ -16082,7 +16333,6 @@ $as_echo "#define HAS_WORKING_SEMAPHORE 1" >>confdefs.h
 else
   { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
-
 fi
 rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
   conftest.$ac_objext conftest.beam conftest.$ac_ext
@@ -16434,7 +16684,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 else
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
- void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;}
+ void __attribute__(()) *test(void *muffin, ...) ;
 int
 main ()
 {
@@ -16502,7 +16752,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 else
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
- void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;}
+ void __attribute__(()) *test(void *muffin, ...) ;
 int
 main ()
 {
@@ -16570,7 +16820,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 else
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
- void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;}
+ void __attribute__(()) *test(void *muffin, ...) ;
 int
 main ()
 {
@@ -16638,7 +16888,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 else
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
- void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;}
+ void __attribute__(()) *test(void *muffin, ...) ;
 int
 main ()
 {
@@ -16706,7 +16956,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 else
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
- void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;}
+ void __attribute__(()) *test(void *muffin, ...) ;
 int
 main ()
 {
@@ -16774,7 +17024,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 else
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
- void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;}
+ void __attribute__(()) *test(void *muffin, ...) ;
 int
 main ()
 {
@@ -16842,7 +17092,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 else
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
- void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;}
+ void __attribute__(()) *test(void *muffin, ...) ;
 int
 main ()
 {
@@ -16910,7 +17160,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 else
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
- void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;}
+ void __attribute__(()) *test(void *muffin, ...) ;
 int
 main ()
 {
@@ -16978,7 +17228,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 else
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
- void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;}
+ void __attribute__(()) *test(void *muffin, ...) ;
 int
 main ()
 {
@@ -17046,7 +17296,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 else
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
- void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;}
+ void __attribute__(()) *test(void *muffin, ...) ;
 int
 main ()
 {
@@ -17114,7 +17364,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 else
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
- void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;}
+ void __attribute__(()) *test(void *muffin, ...) ;
 int
 main ()
 {
@@ -17216,7 +17466,7 @@ fi
 
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -Wtrampolines support" >&5
 $as_echo_n "checking for -Wtrampolines support... " >&6; }
-if $(${CC} -Wtrampolines -S -o /dev/null -xc /dev/null > /dev/null 2>&1); then
+if $(${CC} -Wtrampolines -Werror -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_TRAMPOLINES=-Wtrampolines
@@ -17285,31 +17535,6 @@ $as_echo "no" >&6; }
 fi
 
 
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for -fnested-functions" >&5
-$as_echo_n "checking for -fnested-functions... " >&6; }
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-
-int
-main ()
-{
-auto void foo(void); void foo(void) {}
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-	AST_NESTED_FUNCTIONS=
-else
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: required" >&5
-$as_echo "required" >&6; }
-	AST_NESTED_FUNCTIONS=-fnested-functions
-
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-
 
 # Check whether --enable-rpath was given.
 if test "${enable_rpath+set}" = set; then :
@@ -23985,6 +24210,215 @@ LIBS="${saved_libs}"
 CPPFLAGS="${saved_cppflags}"
 
 
+if test "x${PBX_PJSIP_GET_DEST_INFO}" != "x1" -a "${USE_PJSIP_GET_DEST_INFO}" != "no"; then
+   pbxlibdir=""
+   # if --with-PJSIP_GET_DEST_INFO=DIR has been specified, use it.
+   if test "x${PJSIP_GET_DEST_INFO_DIR}" != "x"; then
+      if test -d ${PJSIP_GET_DEST_INFO_DIR}/lib; then
+         pbxlibdir="-L${PJSIP_GET_DEST_INFO_DIR}/lib"
+      else
+         pbxlibdir="-L${PJSIP_GET_DEST_INFO_DIR}"
+      fi
+   fi
+   pbxfuncname="pjsip_get_dest_info"
+   if test "x${pbxfuncname}" = "x" ; then   # empty lib, assume only headers
+      AST_PJSIP_GET_DEST_INFO_FOUND=yes
+   else
+      ast_ext_lib_check_save_CFLAGS="${CFLAGS}"
+      CFLAGS="${CFLAGS} $PJPROJECT_CFLAGS"
+      as_ac_Lib=`$as_echo "ac_cv_lib_pjsip_${pbxfuncname}" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lpjsip" >&5
+$as_echo_n "checking for ${pbxfuncname} in -lpjsip... " >&6; }
+if eval \${$as_ac_Lib+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpjsip ${pbxlibdir} $PJPROJECT_LIBS $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  eval "$as_ac_Lib=yes"
+else
+  eval "$as_ac_Lib=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+eval ac_res=\$$as_ac_Lib
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then :
+  AST_PJSIP_GET_DEST_INFO_FOUND=yes
+else
+  AST_PJSIP_GET_DEST_INFO_FOUND=no
+fi
+
+      CFLAGS="${ast_ext_lib_check_save_CFLAGS}"
+   fi
+
+   # now check for the header.
+   if test "${AST_PJSIP_GET_DEST_INFO_FOUND}" = "yes"; then
+      PJSIP_GET_DEST_INFO_LIB="${pbxlibdir} -lpjsip $PJPROJECT_LIBS"
+      # if --with-PJSIP_GET_DEST_INFO=DIR has been specified, use it.
+      if test "x${PJSIP_GET_DEST_INFO_DIR}" != "x"; then
+         PJSIP_GET_DEST_INFO_INCLUDE="-I${PJSIP_GET_DEST_INFO_DIR}/include"
+      fi
+      PJSIP_GET_DEST_INFO_INCLUDE="${PJSIP_GET_DEST_INFO_INCLUDE} $PJPROJECT_CFLAGS"
+      if test "xpjsip.h" = "x" ; then	# no header, assume found
+         PJSIP_GET_DEST_INFO_HEADER_FOUND="1"
+      else				# check for the header
+         ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}"
+         CPPFLAGS="${CPPFLAGS} ${PJSIP_GET_DEST_INFO_INCLUDE}"
+         ac_fn_c_check_header_mongrel "$LINENO" "pjsip.h" "ac_cv_header_pjsip_h" "$ac_includes_default"
+if test "x$ac_cv_header_pjsip_h" = xyes; then :
+  PJSIP_GET_DEST_INFO_HEADER_FOUND=1
+else
+  PJSIP_GET_DEST_INFO_HEADER_FOUND=0
+fi
+
+
+         CPPFLAGS="${ast_ext_lib_check_saved_CPPFLAGS}"
+      fi
+      if test "x${PJSIP_GET_DEST_INFO_HEADER_FOUND}" = "x0" ; then
+         PJSIP_GET_DEST_INFO_LIB=""
+         PJSIP_GET_DEST_INFO_INCLUDE=""
+      else
+         if test "x${pbxfuncname}" = "x" ; then		# only checking headers -> no library
+            PJSIP_GET_DEST_INFO_LIB=""
+         fi
+         PBX_PJSIP_GET_DEST_INFO=1
+         cat >>confdefs.h <<_ACEOF
+#define HAVE_PJSIP_GET_DEST_INFO 1
+_ACEOF
+
+      fi
+   fi
+fi
+
+
+
+if test "x${PBX_PJ_SSL_CERT_LOAD_FROM_FILES2}" != "x1" -a "${USE_PJ_SSL_CERT_LOAD_FROM_FILES2}" != "no"; then
+   pbxlibdir=""
+   # if --with-PJ_SSL_CERT_LOAD_FROM_FILES2=DIR has been specified, use it.
+   if test "x${PJ_SSL_CERT_LOAD_FROM_FILES2_DIR}" != "x"; then
+      if test -d ${PJ_SSL_CERT_LOAD_FROM_FILES2_DIR}/lib; then
+         pbxlibdir="-L${PJ_SSL_CERT_LOAD_FROM_FILES2_DIR}/lib"
+      else
+         pbxlibdir="-L${PJ_SSL_CERT_LOAD_FROM_FILES2_DIR}"
+      fi
+   fi
+   pbxfuncname="pj_ssl_cert_load_from_files2"
+   if test "x${pbxfuncname}" = "x" ; then   # empty lib, assume only headers
+      AST_PJ_SSL_CERT_LOAD_FROM_FILES2_FOUND=yes
+   else
+      ast_ext_lib_check_save_CFLAGS="${CFLAGS}"
+      CFLAGS="${CFLAGS} $PJPROJECT_CFLAGS"
+      as_ac_Lib=`$as_echo "ac_cv_lib_pj_${pbxfuncname}" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lpj" >&5
+$as_echo_n "checking for ${pbxfuncname} in -lpj... " >&6; }
+if eval \${$as_ac_Lib+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpj ${pbxlibdir} $PJPROJECT_LIBS $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  eval "$as_ac_Lib=yes"
+else
+  eval "$as_ac_Lib=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+eval ac_res=\$$as_ac_Lib
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then :
+  AST_PJ_SSL_CERT_LOAD_FROM_FILES2_FOUND=yes
+else
+  AST_PJ_SSL_CERT_LOAD_FROM_FILES2_FOUND=no
+fi
+
+      CFLAGS="${ast_ext_lib_check_save_CFLAGS}"
+   fi
+
+   # now check for the header.
+   if test "${AST_PJ_SSL_CERT_LOAD_FROM_FILES2_FOUND}" = "yes"; then
+      PJ_SSL_CERT_LOAD_FROM_FILES2_LIB="${pbxlibdir} -lpj $PJPROJECT_LIBS"
+      # if --with-PJ_SSL_CERT_LOAD_FROM_FILES2=DIR has been specified, use it.
+      if test "x${PJ_SSL_CERT_LOAD_FROM_FILES2_DIR}" != "x"; then
+         PJ_SSL_CERT_LOAD_FROM_FILES2_INCLUDE="-I${PJ_SSL_CERT_LOAD_FROM_FILES2_DIR}/include"
+      fi
+      PJ_SSL_CERT_LOAD_FROM_FILES2_INCLUDE="${PJ_SSL_CERT_LOAD_FROM_FILES2_INCLUDE} $PJPROJECT_CFLAGS"
+      if test "xpjlib.h" = "x" ; then	# no header, assume found
+         PJ_SSL_CERT_LOAD_FROM_FILES2_HEADER_FOUND="1"
+      else				# check for the header
+         ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}"
+         CPPFLAGS="${CPPFLAGS} ${PJ_SSL_CERT_LOAD_FROM_FILES2_INCLUDE}"
+         ac_fn_c_check_header_mongrel "$LINENO" "pjlib.h" "ac_cv_header_pjlib_h" "$ac_includes_default"
+if test "x$ac_cv_header_pjlib_h" = xyes; then :
+  PJ_SSL_CERT_LOAD_FROM_FILES2_HEADER_FOUND=1
+else
+  PJ_SSL_CERT_LOAD_FROM_FILES2_HEADER_FOUND=0
+fi
+
+
+         CPPFLAGS="${ast_ext_lib_check_saved_CPPFLAGS}"
+      fi
+      if test "x${PJ_SSL_CERT_LOAD_FROM_FILES2_HEADER_FOUND}" = "x0" ; then
+         PJ_SSL_CERT_LOAD_FROM_FILES2_LIB=""
+         PJ_SSL_CERT_LOAD_FROM_FILES2_INCLUDE=""
+      else
+         if test "x${pbxfuncname}" = "x" ; then		# only checking headers -> no library
+            PJ_SSL_CERT_LOAD_FROM_FILES2_LIB=""
+         fi
+         PBX_PJ_SSL_CERT_LOAD_FROM_FILES2=1
+         cat >>confdefs.h <<_ACEOF
+#define HAVE_PJ_SSL_CERT_LOAD_FROM_FILES2 1
+_ACEOF
+
+      fi
+   fi
+fi
+
+
+
+
 if test "x${PBX_POPT}" != "x1" -a "${USE_POPT}" != "no"; then
    pbxlibdir=""
    # if --with-POPT=DIR has been specified, use it.
@@ -29113,10 +29547,8 @@ fi
 
 
 if test "${PBX_SQLITE3}" != 1; then
-	{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: *** Asterisk now uses SQLite3 for the internal Asterisk database." >&5
-$as_echo "$as_me: WARNING: *** Asterisk now uses SQLite3 for the internal Asterisk database." >&2;}
-	{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: *** Please install the SQLite3 development package." >&5
-$as_echo "$as_me: WARNING: *** Please install the SQLite3 development package." >&2;}
+	as_fn_error $? "*** Asterisk now uses SQLite3 for the internal Asterisk database." "$LINENO" 5
+	as_fn_error $? "*** Please install the SQLite3 development package." "$LINENO" 5
 	exit 1
 fi
 
@@ -29852,6 +30284,149 @@ fi
 
 fi
 
+if test "$PBX_OPENSSL" = "1";
+then
+
+    if test "x${PBX_OPENSSL_ECDH_AUTO}" != "x1" -a "${USE_OPENSSL_ECDH_AUTO}" != "no"; then
+        { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SSL_CTX_set_ecdh_auto declared in openssl/ssl.h" >&5
+$as_echo_n "checking for SSL_CTX_set_ecdh_auto declared in openssl/ssl.h... " >&6; }
+        saved_cppflags="${CPPFLAGS}"
+        if test "x${OPENSSL_ECDH_AUTO_DIR}" != "x"; then
+            OPENSSL_ECDH_AUTO_INCLUDE="-I${OPENSSL_ECDH_AUTO_DIR}/include"
+        fi
+        CPPFLAGS="${CPPFLAGS} ${OPENSSL_ECDH_AUTO_INCLUDE}"
+
+        cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+ #include <openssl/ssl.h>
+int
+main ()
+{
+#if !defined(SSL_CTX_set_ecdh_auto)
+                                    (void) SSL_CTX_set_ecdh_auto;
+                                #endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+     { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                PBX_OPENSSL_ECDH_AUTO=1
+
+$as_echo "#define HAVE_OPENSSL_ECDH_AUTO 1" >>confdefs.h
+
+
+
+else
+     { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+        CPPFLAGS="${saved_cppflags}"
+    fi
+
+fi
+
+if test "$PBX_OPENSSL" = "1";
+then
+
+    if test "x${PBX_SSL_OP_NO_TLSV1_1}" != "x1"; then
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for SSL_OP_NO_TLSv1_1 in openssl/ssl.h" >&5
+$as_echo_n "checking for SSL_OP_NO_TLSv1_1 in openssl/ssl.h... " >&6; }
+	saved_cppflags="${CPPFLAGS}"
+	if test "x${SSL_OP_NO_TLSV1_1_DIR}" != "x"; then
+	    SSL_OP_NO_TLSV1_1_INCLUDE="-I${SSL_OP_NO_TLSV1_1_DIR}/include"
+	fi
+	CPPFLAGS="${CPPFLAGS} ${SSL_OP_NO_TLSV1_1_INCLUDE}"
+
+	cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+ #include <openssl/ssl.h>
+int
+main ()
+{
+#if defined(SSL_OP_NO_TLSv1_1)
+				int foo = 0;
+			        #else
+			        int foo = bar;
+			        #endif
+				0
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+     { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+		PBX_SSL_OP_NO_TLSV1_1=1
+
+$as_echo "#define HAVE_SSL_OP_NO_TLSV1_1 1" >>confdefs.h
+
+
+
+else
+     { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+	CPPFLAGS="${saved_cppflags}"
+    fi
+
+
+
+    if test "x${PBX_SSL_OP_NO_TLSV1_2}" != "x1"; then
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for SSL_OP_NO_TLSv1_2 in openssl/ssl.h" >&5
+$as_echo_n "checking for SSL_OP_NO_TLSv1_2 in openssl/ssl.h... " >&6; }
+	saved_cppflags="${CPPFLAGS}"
+	if test "x${SSL_OP_NO_TLSV1_2_DIR}" != "x"; then
+	    SSL_OP_NO_TLSV1_2_INCLUDE="-I${SSL_OP_NO_TLSV1_2_DIR}/include"
+	fi
+	CPPFLAGS="${CPPFLAGS} ${SSL_OP_NO_TLSV1_2_INCLUDE}"
+
+	cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+ #include <openssl/ssl.h>
+int
+main ()
+{
+#if defined(SSL_OP_NO_TLSv1_2)
+				int foo = 0;
+			        #else
+			        int foo = bar;
+			        #endif
+				0
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+     { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+		PBX_SSL_OP_NO_TLSV1_2=1
+
+$as_echo "#define HAVE_SSL_OP_NO_TLSV1_2 1" >>confdefs.h
+
+
+
+else
+     { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+	CPPFLAGS="${saved_cppflags}"
+    fi
+
+
+fi
+
 
 if test "x${PBX_SRTP}" != "x1" -a "${USE_SRTP}" != "no"; then
    pbxlibdir=""
@@ -31771,14 +32346,12 @@ fi
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_file__sbin_launchd" >&5
 $as_echo "$ac_cv_file__sbin_launchd" >&6; }
 if test "x$ac_cv_file__sbin_launchd" = xyes; then :
+  PBX_LAUNCHD=1
 
 $as_echo "#define HAVE_SBIN_LAUNCHD 1" >>confdefs.h
 
 fi
 
-  if test "${HAVE_SBIN_LAUNCHD}" = 1; then
-    PBX_LAUNCHD=1
-  fi
 fi
 
 
diff --git a/configure.ac b/configure.ac
index 3aa8c28..76072f9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -20,7 +20,7 @@ AC_CONFIG_SRCDIR([main/asterisk.c])
 AC_CONFIG_AUX_DIR(`pwd`)
 
 AC_COPYRIGHT("Asterisk")
-AC_REVISION($Revision: 426234 $)
+AC_REVISION($Revision$)
 
 # preserve any CFLAGS or LDFLAGS that may be set
 # NOTE: This must be done before calling any macros that end up
@@ -386,6 +386,9 @@ AC_ARG_ENABLE([coverage],
 	esac])
 AC_SUBST(AST_CODE_COVERAGE)
 
+AST_CHECK_RAII()
+AST_CHECK_STRSEP_ARRAY_BOUNDS()
+
 # AST_EXT_LIB_SETUP is used to tell configure to handle variables for
 # various packages.
 # $1 is the prefix for the variables in makeopts and autoconfig.h
@@ -411,6 +414,7 @@ AST_EXT_LIB_SETUP([CRYPT], [password and data encryption], [crypt])
 AST_EXT_LIB_SETUP([CRYPTO], [OpenSSL Cryptography], [crypto])
 AST_EXT_LIB_SETUP_OPTIONAL([OPENSSL_SRTP], [OpenSSL SRTP Extension Support], [CRYPTO], [crypto])
 AST_EXT_LIB_SETUP_OPTIONAL([OPENSSL_EC], [OpenSSL Elliptic Curve Support], [CRYPTO], [crypto])
+AST_EXT_LIB_SETUP_OPTIONAL([OPENSSL_ECDH_AUTO], [OpenSSL Auto ECDH Support], [CRYPTO], [crypto])
 AST_EXT_LIB_SETUP([DAHDI], [DAHDI], [dahdi])
 AST_EXT_LIB_SETUP([FFMPEG], [Ffmpeg and avcodec], [avcodec])
 AST_EXT_LIB_SETUP([GSM], [External GSM], [gsm], [, use 'internal' GSM otherwise])
@@ -456,6 +460,8 @@ AST_EXT_LIB_SETUP([PJPROJECT], [PJPROJECT], [pjproject])
 AST_EXT_LIB_SETUP([POPT], [popt], [popt])
 AST_EXT_LIB_SETUP_OPTIONAL([PJ_TRANSACTION_GRP_LOCK], [PJSIP Transaction Group Lock Support], [PJPROJECT], [pjsip])
 AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_REPLACE_MEDIA_STREAM], [PJSIP Media Stream Replacement Support], [PJPROJECT], [pjsip])
+AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_GET_DEST_INFO], [pjsip_get_dest_info support], [PJPROJECT], [pjsip])
+AST_EXT_LIB_SETUP_OPTIONAL([PJ_SSL_CERT_LOAD_FROM_FILES2], [pj_ssl_cert_load_from_files2 support], [PJPROJECT], [pjsip])
 AST_EXT_LIB_SETUP([PORTAUDIO], [PortAudio], [portaudio])
 AST_EXT_LIB_SETUP([PRI], [ISDN PRI], [pri])
 AST_EXT_LIB_SETUP_OPTIONAL([PRI_SETUP_ACK_INBAND], [ISDN PRI progress inband ie in SETUP ACK], [PRI], [pri])
@@ -658,7 +664,25 @@ AC_FUNC_STRNLEN
 AC_FUNC_STRTOD
 AC_FUNC_UTIME_NULL
 AC_FUNC_VPRINTF
-AC_CHECK_FUNCS([asprintf atexit closefrom dup2 eaccess endpwent euidaccess ffsll ftruncate getcwd gethostbyname gethostname getloadavg gettimeofday glob htonll ioperm inet_ntoa isascii memchr memmove memset mkdir mkdtemp munmap ntohll newlocale ppoll putenv re_comp regcomp select setenv socket strcasecmp strcasestr strchr strcspn strdup strerror strlcat strlcpy strncasecmp strndup strnlen strrchr strsep strspn strstr strtod strtol strtold strtoq unsetenv utime vasprintf getpeereid sysctl [...]
+AC_CHECK_FUNCS([asprintf atexit closefrom dup2 eaccess endpwent euidaccess ffsll ftruncate getcwd gethostbyname gethostname getloadavg gettimeofday glob ioperm inet_ntoa isascii memchr memmove memset mkdir mkdtemp munmap newlocale ppoll putenv re_comp regcomp select setenv socket strcasecmp strcasestr strchr strcspn strdup strerror strlcat strlcpy strncasecmp strndup strnlen strrchr strsep strspn strstr strtod strtol strtold strtoq unsetenv utime vasprintf getpeereid sysctl swapctl])
+
+AC_MSG_CHECKING(for htonll)
+AC_LINK_IFELSE(
+	[AC_LANG_PROGRAM([#include <arpa/inet.h>],
+		[return htonll(0);])],
+	AC_MSG_RESULT(yes)
+	AC_DEFINE(HAVE_HTONLL, 1, [Define to 1 if arpa/inet.h includes a htonll definition.]),
+	AC_MSG_RESULT(no)
+)
+
+AC_MSG_CHECKING(for ntohll)
+AC_LINK_IFELSE(
+	[AC_LANG_PROGRAM([#include <arpa/inet.h>],
+		[return ntohll(0);])],
+	AC_MSG_RESULT(yes)
+	AC_DEFINE(HAVE_NTOHLL, 1, [Define to 1 if arpa/inet.h includes a ntohll definition.]),
+	AC_MSG_RESULT(no)
+)
 
 # NOTE: we use AC_CHECK_LIB to get -lm into the arguments for later checks,
 # so that AC_CHECK_FUNCS can detect functions in that library.
@@ -883,7 +907,10 @@ AC_RUN_IFELSE(
 		[sem_t sem; return sem_init(&sem, 0, 0);])],
 	AC_MSG_RESULT(yes)
 	AC_DEFINE([HAS_WORKING_SEMAPHORE], 1, [Define to 1 if anonymous semaphores work.]),
-	AC_MSG_RESULT(no)
+	AC_MSG_RESULT(no),
+	AC_MSG_RESULT(cross-compile)
+	AC_MSG_NOTICE([WARNING: result yes guessed because of cross compilation])
+	AC_DEFINE([HAS_WORKING_SEMAPHORE], 1, [Define to 1 if anonymous semaphores work.])
 )
 
 LIBS="$save_LIBS"
@@ -1055,7 +1082,7 @@ fi
 AC_SUBST(AST_DECLARATION_AFTER_STATEMENT)
 
 AC_MSG_CHECKING(for -Wtrampolines support)
-if $(${CC} -Wtrampolines -S -o /dev/null -xc /dev/null > /dev/null 2>&1); then
+if $(${CC} -Wtrampolines -Werror -S -o /dev/null -xc /dev/null > /dev/null 2>&1); then
 	AC_MSG_RESULT(yes)
 	AST_TRAMPOLINES=-Wtrampolines
 else
@@ -1109,17 +1136,6 @@ else
 fi
 AC_SUBST(AST_NATIVE_ARCH)
 
-dnl Nested functions required for RAII implementation
-AC_MSG_CHECKING(for -fnested-functions)
-AC_COMPILE_IFELSE(
-	dnl Prototype needed due to http://gcc.gnu.org/bugzilla/show_bug.cgi?id=36774
-	[AC_LANG_PROGRAM([], [auto void foo(void); void foo(void) {}])],
-	AC_MSG_RESULT(no)
-	[AST_NESTED_FUNCTIONS=],
-	AC_MSG_RESULT(required)
-	[AST_NESTED_FUNCTIONS=-fnested-functions]
-)
-AC_SUBST(AST_NESTED_FUNCTIONS)
 
 dnl Check to see if rpath should be set in LDFLAGS
 AC_ARG_ENABLE(rpath,
@@ -2071,6 +2087,9 @@ AST_C_COMPILE_CHECK([PJSIP_REPLACE_MEDIA_STREAM], [pjmedia_mod_offer_flag flag =
 LIBS="${saved_libs}"
 CPPFLAGS="${saved_cppflags}"
 
+AST_EXT_LIB_CHECK([PJSIP_GET_DEST_INFO], [pjsip], [pjsip_get_dest_info], [pjsip.h], [$PJPROJECT_LIBS], [$PJPROJECT_CFLAGS])
+AST_EXT_LIB_CHECK([PJ_SSL_CERT_LOAD_FROM_FILES2], [pj], [pj_ssl_cert_load_from_files2], [pjlib.h], [$PJPROJECT_LIBS], [$PJPROJECT_CFLAGS])
+
 AST_EXT_LIB_CHECK([POPT], [popt], [poptStrerror], [popt.h])
 
 AST_EXT_LIB_CHECK([PORTAUDIO], [portaudio], [Pa_GetDeviceCount], [portaudio.h])
@@ -2219,8 +2238,8 @@ AST_EXT_LIB_CHECK([SQLITE], [sqlite], [sqlite_exec], [sqlite.h])
 AST_EXT_LIB_CHECK([SQLITE3], [sqlite3], [sqlite3_open], [sqlite3.h], [${PTHREAD_LIBS}], [${PTHREAD_CFLAGS}])
 
 if test "${PBX_SQLITE3}" != 1; then
-	AC_MSG_WARN(*** Asterisk now uses SQLite3 for the internal Asterisk database.)
-	AC_MSG_WARN(*** Please install the SQLite3 development package.)
+	AC_MSG_ERROR(*** Asterisk now uses SQLite3 for the internal Asterisk database.)
+	AC_MSG_ERROR(*** Please install the SQLite3 development package.)
 	exit 1
 fi
 
@@ -2265,6 +2284,17 @@ then
 	AST_EXT_LIB_CHECK([OPENSSL_EC], [ssl], [EC_KEY_new_by_curve_name], [openssl/ec.h], [-lcrypto])
 fi
 
+if test "$PBX_OPENSSL" = "1";
+then
+        AST_C_DECLARE_CHECK([OPENSSL_ECDH_AUTO], [SSL_CTX_set_ecdh_auto], [openssl/ssl.h])
+fi
+
+if test "$PBX_OPENSSL" = "1";
+then
+        AST_C_DEFINE_CHECK([SSL_OP_NO_TLSV1_1], [SSL_OP_NO_TLSv1_1], [openssl/ssl.h])
+        AST_C_DEFINE_CHECK([SSL_OP_NO_TLSV1_2], [SSL_OP_NO_TLSv1_2], [openssl/ssl.h])
+fi
+
 AST_EXT_LIB_CHECK([SRTP], [srtp], [srtp_init], [srtp/srtp.h])
 
 if test "$PBX_SRTP" = "1";
@@ -2424,10 +2454,9 @@ AST_EXT_LIB_CHECK([X11], [X11], [XOpenDisplay], [X11/Xlib.h],, [-I/usr/X11R6/inc
 PBX_LAUNCHD=0
 if test "${cross_compiling}" = "no";
 then
-  AC_CHECK_FILE(/sbin/launchd, AC_DEFINE([HAVE_SBIN_LAUNCHD], 1, [Define to 1 if your system has /sbin/launchd.]))
-  if test "${HAVE_SBIN_LAUNCHD}" = 1; then
-    PBX_LAUNCHD=1
-  fi
+  AC_CHECK_FILE(/sbin/launchd,
+    [PBX_LAUNCHD=1]
+    AC_DEFINE([HAVE_SBIN_LAUNCHD], 1, [Define to 1 if your system has /sbin/launchd.]))
 fi
 AC_SUBST(PBX_LAUNCHD)
 
diff --git a/contrib/ast-db-manage/config/versions/154177371065_add_default_from_user.py b/contrib/ast-db-manage/config/versions/154177371065_add_default_from_user.py
new file mode 100644
index 0000000..7e6cf99
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/154177371065_add_default_from_user.py
@@ -0,0 +1,22 @@
+"""add default_from_user
+
+Revision ID: 154177371065
+Revises: 26f10cadc157
+Create Date: 2015-09-04 14:13:59.195013
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '154177371065'
+down_revision = '26f10cadc157'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+    op.add_column('ps_globals', sa.Column('default_from_user', sa.String(80)))
+
+
+def downgrade():
+    op.drop_column('ps_globals', 'default_from_user')
diff --git a/contrib/ast-db-manage/config/versions/189a235b3fd7_add_keep_alive_interval.py b/contrib/ast-db-manage/config/versions/189a235b3fd7_add_keep_alive_interval.py
new file mode 100644
index 0000000..aa52171
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/189a235b3fd7_add_keep_alive_interval.py
@@ -0,0 +1,22 @@
+"""add_keep_alive_interval
+
+Revision ID: 189a235b3fd7
+Revises: 28ce1e718f05
+Create Date: 2015-12-16 11:23:16.994523
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '189a235b3fd7'
+down_revision = '28ce1e718f05'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+    op.add_column('ps_globals', sa.Column('keep_alive_interval', sa.Integer))
+
+
+def downgrade():
+    op.drop_column('ps_globals', 'keep_alive_interval')
diff --git a/contrib/ast-db-manage/config/versions/23530d604b96_add_rpid_immediate.py b/contrib/ast-db-manage/config/versions/23530d604b96_add_rpid_immediate.py
new file mode 100755
index 0000000..dc0c01c
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/23530d604b96_add_rpid_immediate.py
@@ -0,0 +1,48 @@
+#
+# Asterisk -- An open source telephony toolkit.
+#
+# Copyright (C) 2015, Richard Mudgett
+#
+# 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.
+#
+
+"""add rpid_immediate
+
+Revision ID: 23530d604b96
+Revises: 45e3f47c6c44
+Create Date: 2015-03-18 17:41:58.055412
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '23530d604b96'
+down_revision = '45e3f47c6c44'
+
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.dialects.postgresql import ENUM
+
+YESNO_NAME = 'yesno_values'
+YESNO_VALUES = ['yes', 'no']
+
+def upgrade():
+    ############################# Enums ##############################
+
+    # yesno_values have already been created, so use postgres enum object
+    # type to get around "already created" issue - works okay with mysql
+    yesno_values = ENUM(*YESNO_VALUES, name=YESNO_NAME, create_type=False)
+
+    op.add_column('ps_endpoints', sa.Column('rpid_immediate', yesno_values))
+
+def downgrade():
+    op.drop_column('ps_endpoints', 'rpid_immediate')
diff --git a/contrib/ast-db-manage/config/versions/26f10cadc157_add_pjsip_timeout_options.py b/contrib/ast-db-manage/config/versions/26f10cadc157_add_pjsip_timeout_options.py
new file mode 100644
index 0000000..8972d80
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/26f10cadc157_add_pjsip_timeout_options.py
@@ -0,0 +1,24 @@
+"""add pjsip timeout options
+
+Revision ID: 26f10cadc157
+Revises: 498357a710ae
+Create Date: 2015-07-21 07:45:00.696965
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '26f10cadc157'
+down_revision = '498357a710ae'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+    op.add_column('ps_endpoints', sa.Column('rtp_timeout', sa.Integer))
+    op.add_column('ps_endpoints', sa.Column('rtp_timeout_hold', sa.Integer))
+
+
+def downgrade():
+    op.drop_column('ps_endpoints', 'rtp_timeout')
+    op.drop_column('ps_endpoints', 'rtp_timeout_hold')
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
new file mode 100644
index 0000000..ad36bd9
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/28b8e71e541f_add_g726_non_standard.py
@@ -0,0 +1,30 @@
+"""add g726_non_standard
+
+Revision ID: 28b8e71e541f
+Revises: a541e0b5e89
+Create Date: 2015-06-12 16:07:08.609628
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '28b8e71e541f'
+down_revision = 'a541e0b5e89'
+
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.dialects.postgresql import ENUM
+
+YESNO_NAME = 'yesno_values'
+YESNO_VALUES = ['yes', 'no']
+
+def upgrade():
+    ############################# Enums ##############################
+
+    # yesno_values have already been created, so use postgres enum object
+    # type to get around "already created" issue - works okay with mysql
+    yesno_values = ENUM(*YESNO_VALUES, name=YESNO_NAME, create_type=False)
+    op.add_column('ps_endpoints', sa.Column('g726_non_standard', yesno_values))
+
+
+def downgrade():
+    op.drop_column('ps_endpoints', 'g726_non_standard')
diff --git a/contrib/ast-db-manage/config/versions/28ce1e718f05_add_fatal_response_interval.py b/contrib/ast-db-manage/config/versions/28ce1e718f05_add_fatal_response_interval.py
new file mode 100644
index 0000000..8c499ae
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/28ce1e718f05_add_fatal_response_interval.py
@@ -0,0 +1,22 @@
+"""add fatal_response_interval
+
+Revision ID: 28ce1e718f05
+Revises: 154177371065
+Create Date: 2015-10-20 17:57:45.560585
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '28ce1e718f05'
+down_revision = '154177371065'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+    op.add_column('ps_registrations', sa.Column('fatal_retry_interval', sa.Integer))
+
+
+def downgrade():
+    op.drop_column('ps_registrations', 'fatal_retry_interval')
diff --git a/contrib/ast-db-manage/config/versions/2d078ec071b7_increaes_contact_column_size.py b/contrib/ast-db-manage/config/versions/2d078ec071b7_increaes_contact_column_size.py
new file mode 100644
index 0000000..2ade86f
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/2d078ec071b7_increaes_contact_column_size.py
@@ -0,0 +1,22 @@
+"""increaes_contact_column_size
+
+Revision ID: 2d078ec071b7
+Revises: 189a235b3fd7
+Create Date: 2015-12-16 11:26:54.218985
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '2d078ec071b7'
+down_revision = '189a235b3fd7'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+    op.alter_column('ps_aors', 'contact', type_=sa.String(255))
+
+
+def downgrade():
+    op.alter_column('ps_aors', 'contact', type_=sa.String(40))
diff --git a/contrib/ast-db-manage/config/versions/31cd4f4891ec_add_auto_dtmf_mode.py b/contrib/ast-db-manage/config/versions/31cd4f4891ec_add_auto_dtmf_mode.py
new file mode 100644
index 0000000..0d84390
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/31cd4f4891ec_add_auto_dtmf_mode.py
@@ -0,0 +1,64 @@
+"""Add auto DTMF mode
+
+Revision ID: 31cd4f4891ec
+Revises: 23530d604b96
+Create Date: 2015-04-10 12:36:51.619419
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '31cd4f4891ec'
+down_revision = '23530d604b96'
+
+from alembic import op
+from sqlalchemy.dialects.postgresql import ENUM
+import sqlalchemy as sa
+
+OLD_ENUM = ['rfc4733', 'inband', 'info']
+NEW_ENUM = ['rfc4733', 'inband', 'info', 'auto']
+
+old_type = sa.Enum(*OLD_ENUM, name='pjsip_dtmf_mode_values')
+new_type = sa.Enum(*NEW_ENUM, name='pjsip_dtmf_mode_values_v2')
+
+tcr = sa.sql.table('ps_endpoints', sa.Column('dtmf_mode', new_type,
+                   nullable=True))
+
+def upgrade():
+    context = op.get_context()
+
+    # Upgrading to this revision WILL clear your directmedia values.
+    if context.bind.dialect.name != 'postgresql':
+        op.alter_column('ps_endpoints', 'dtmf_mode',
+                        type_=new_type,
+                        existing_type=old_type)
+    else:
+        enum = ENUM('rfc4733', 'inband', 'info', 'auto',
+                    name='pjsip_dtmf_mode_values_v2')
+        enum.create(op.get_bind(), checkfirst=False)
+
+        op.execute('ALTER TABLE ps_endpoints ALTER COLUMN dtmf_mode TYPE'
+                   ' pjsip_dtmf_mode_values_v2 USING'
+                   ' dtmf_mode::text::pjsip_dtmf_mode_values_v2')
+
+        ENUM(name="pjsip_dtmf_mode_values").drop(op.get_bind(), checkfirst=False)
+
+def downgrade():
+    context = op.get_context()
+
+    op.execute(tcr.update().where(tcr.c.directmedia==u'outgoing')
+               .values(directmedia=None))
+
+    if context.bind.dialect.name != 'postgresql':
+        op.alter_column('ps_endpoints', 'dtmf_mode',
+                        type_=old_type,
+                        existing_type=new_type)
+    else:
+        enum = ENUM('rfc4733', 'inband', 'info',
+                    name='pjsip_dtmf_mode_values')
+        enum.create(op.get_bind(), checkfirst=False)
+
+        op.execute('ALTER TABLE ps_endpoints ALTER COLUMN dtmf_mode TYPE'
+                   ' pjsip_dtmf_mode_values USING'
+                   ' dtmf_mode::text::pjsip_dtmf_mode_values')
+
+        ENUM(name="pjsip_dtmf_mode_values_v2").drop(op.get_bind(), checkfirst=False)
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
new file mode 100644
index 0000000..ea2b291
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/371a3bf4143e_add_user_eq_phone_option_to_pjsip.py
@@ -0,0 +1,30 @@
+"""add user_eq_phone option to pjsip
+
+Revision ID: 371a3bf4143e
+Revises: 10aedae86a32
+Create Date: 2014-10-13 13:46:24.474675
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '371a3bf4143e'
+down_revision = 'eb88a14f2a'
+
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.dialects.postgresql import ENUM
+
+YESNO_NAME = 'yesno_values'
+YESNO_VALUES = ['yes', 'no']
+
+def upgrade():
+    ############################# Enums ##############################
+
+    # yesno_values have already been created, so use postgres enum object
+    # type to get around "already created" issue - works okay with mysql
+    yesno_values = ENUM(*YESNO_VALUES, name=YESNO_NAME, create_type=False)
+
+    op.add_column('ps_endpoints', sa.Column('user_eq_phone', yesno_values))
+
+def downgrade():
+    op.drop_column('ps_endpoints', 'user_eq_phone')
diff --git a/contrib/ast-db-manage/config/versions/45e3f47c6c44_add_pjsip_endpoint_identifier_order.py b/contrib/ast-db-manage/config/versions/45e3f47c6c44_add_pjsip_endpoint_identifier_order.py
new file mode 100644
index 0000000..213da92
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/45e3f47c6c44_add_pjsip_endpoint_identifier_order.py
@@ -0,0 +1,21 @@
+"""add pjsip endpoint_identifier_order
+
+Revision ID: 45e3f47c6c44
+Revises: 371a3bf4143e
+Create Date: 2015-03-02 09:32:20.632015
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '45e3f47c6c44'
+down_revision = '371a3bf4143e'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+    op.add_column('ps_globals', sa.Column('endpoint_identifier_order', sa.String(40)))
+
+def downgrade():
+    op.drop_column('ps_globals', 'endpoint_identifier_order')
diff --git a/contrib/ast-db-manage/config/versions/461d7d691209_add_pjsip_qualify_timeout.py b/contrib/ast-db-manage/config/versions/461d7d691209_add_pjsip_qualify_timeout.py
new file mode 100644
index 0000000..9600c04
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/461d7d691209_add_pjsip_qualify_timeout.py
@@ -0,0 +1,25 @@
+"""add pjsip qualify_timeout
+
+Revision ID: 461d7d691209
+Revises: 31cd4f4891ec
+Create Date: 2015-04-15 13:54:08.047851
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '461d7d691209'
+down_revision = '31cd4f4891ec'
+
+from alembic import op
+import sqlalchemy as sa
+
+def upgrade():
+    op.add_column('ps_aors', sa.Column('qualify_timeout', sa.Integer))
+    op.add_column('ps_contacts', sa.Column('qualify_timeout', sa.Integer))
+    pass
+
+
+def downgrade():
+    op.drop_column('ps_aors', 'qualify_timeout')
+    op.drop_column('ps_contacts', 'qualify_timeout')
+    pass
diff --git a/contrib/ast-db-manage/config/versions/498357a710ae_add_rtp_keepalive.py b/contrib/ast-db-manage/config/versions/498357a710ae_add_rtp_keepalive.py
new file mode 100644
index 0000000..5a4f470
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/498357a710ae_add_rtp_keepalive.py
@@ -0,0 +1,22 @@
+"""Add RTP keepalive
+
+Revision ID: 498357a710ae
+Revises: 28b8e71e541f
+Create Date: 2015-07-10 16:42:12.244421
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '498357a710ae'
+down_revision = '28b8e71e541f'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+    op.add_column('ps_endpoints', sa.Column('rtp_keepalive', sa.Integer))
+
+
+def downgrade():
+    op.drop_column('ps_endpoints', 'rtp_keepalive')
diff --git a/contrib/ast-db-manage/config/versions/a541e0b5e89_add_pjsip_max_initial_qualify_time.py b/contrib/ast-db-manage/config/versions/a541e0b5e89_add_pjsip_max_initial_qualify_time.py
new file mode 100644
index 0000000..0ffd784
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/a541e0b5e89_add_pjsip_max_initial_qualify_time.py
@@ -0,0 +1,20 @@
+"""add pjsip max_initial_qualify_time
+
+Revision ID: a541e0b5e89
+Revises: 461d7d691209
+Create Date: 2015-04-15 14:37:36.424471
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = 'a541e0b5e89'
+down_revision = '461d7d691209'
+
+from alembic import op
+import sqlalchemy as sa
+
+def upgrade():
+    op.add_column('ps_globals', sa.Column('max_initial_qualify_time', sa.Integer))
+
+def downgrade():
+    op.drop_column('ps_globals', 'max_initial_qualify_time')
diff --git a/contrib/editors/asterisk.vim b/contrib/editors/asterisk.vim
index a96a836..dd53a70 100644
--- a/contrib/editors/asterisk.vim
+++ b/contrib/editors/asterisk.vim
@@ -14,7 +14,8 @@ syn sync clear
 syn sync fromstart
 
 syn keyword     asteriskTodo            TODO contained
-syn match       asteriskComment         ";.*" contains=asteriskTodo
+syn match       asteriskComment         "\\\@<!;.*" contains=asteriskTodo
+syn region      asteriskBlockComment    start=/\\\@<!;---\@!/ end=/--;/ contains=asteriskBlockComment,asteriskTodo
 syn match       asteriskContext         "\[.\{-}\]"
 syn match       asteriskExten           "^\s*exten\s*=>\?\s*[^,]\+" contains=asteriskPattern
 syn match       asteriskExten           "^\s*\(register\|channel\|ignorepat\|include\|\(no\)\?load\)\s*=>\?"
@@ -67,6 +68,7 @@ if version >= 508 || !exists("did_conf_syntax_inits")
   endif
 
   HiLink        asteriskComment         Comment
+  HiLink        asteriskBlockComment    Comment
   HiLink        asteriskExten           String
   HiLink        asteriskContext         Preproc
   HiLink        asteriskPattern         Type
diff --git a/contrib/init.d/rc.debian.asterisk b/contrib/init.d/rc.debian.asterisk
index ecd7aff..211737f 100755
--- a/contrib/init.d/rc.debian.asterisk
+++ b/contrib/init.d/rc.debian.asterisk
@@ -1,5 +1,5 @@
 #! /bin/sh
-# $Id: rc.debian.asterisk 379791 2013-01-21 20:41:12Z mjordan $
+# $Id$
 #
 # Mon Jun 04 2007 Iñaki Baz Castillo <ibc at in.ilimit.es>
 # - Eliminated SAFE_ASTERISK since it doesn't work as LSB script (it could require a independent "safe_asterisk" init script).
diff --git a/contrib/init.d/rc.gentoo.asterisk b/contrib/init.d/rc.gentoo.asterisk
index 63cf6f5..04d1c7a 100755
--- a/contrib/init.d/rc.gentoo.asterisk
+++ b/contrib/init.d/rc.gentoo.asterisk
@@ -1,5 +1,5 @@
 #!/sbin/runscript
-# $Id: rc.gentoo.asterisk 379791 2013-01-21 20:41:12Z mjordan $
+# $Id$
 
 ### BEGIN INIT INFO
 # Provides:		asterisk
diff --git a/contrib/init.d/rc.mandriva.asterisk b/contrib/init.d/rc.mandriva.asterisk
index 67393f3..e095ccf 100755
--- a/contrib/init.d/rc.mandriva.asterisk
+++ b/contrib/init.d/rc.mandriva.asterisk
@@ -22,7 +22,7 @@
 # Description:		the Asterisk Open Source PBX
 ### END INIT INFO
 
-# $Id: rc.mandriva.asterisk 379791 2013-01-21 20:41:12Z mjordan $
+# $Id$
 
 TTY=9			# TTY (if you want one) for Asterisk to run on
 CONSOLE=yes		# Whether or not you want a console
diff --git a/contrib/init.d/rc.mandriva.zaptel b/contrib/init.d/rc.mandriva.zaptel
index 4bb3494..14a658b 100755
--- a/contrib/init.d/rc.mandriva.zaptel
+++ b/contrib/init.d/rc.mandriva.zaptel
@@ -9,7 +9,7 @@
 #
 # hide: true
 
-# $Id: rc.mandriva.zaptel 135485 2008-08-04 17:12:15Z tilghman $
+# $Id$
 
 # Source function library.
 . /etc/rc.d/init.d/functions
diff --git a/contrib/init.d/rc.redhat.asterisk b/contrib/init.d/rc.redhat.asterisk
index b04c472..45ea9bc 100755
--- a/contrib/init.d/rc.redhat.asterisk
+++ b/contrib/init.d/rc.redhat.asterisk
@@ -1,5 +1,5 @@
 #!/bin/sh
-# $Id: rc.redhat.asterisk 379791 2013-01-21 20:41:12Z mjordan $
+# $Id$
 #
 # asterisk    Starts, Stops and Reloads Asterisk.
 #
diff --git a/contrib/init.d/rc.slackware.asterisk b/contrib/init.d/rc.slackware.asterisk
index b87efd3..79c2b92 100755
--- a/contrib/init.d/rc.slackware.asterisk
+++ b/contrib/init.d/rc.slackware.asterisk
@@ -6,7 +6,7 @@
 #
 # 03.29.2005 - Initial Version
 #
-# $Id: rc.slackware.asterisk 379791 2013-01-21 20:41:12Z mjordan $
+# $Id$
 
 ### BEGIN INIT INFO
 # Provides:		asterisk
diff --git a/contrib/realtime/sqlserver/mssql_cdr.sql b/contrib/realtime/mssql/mssql_cdr.sql
similarity index 100%
rename from contrib/realtime/sqlserver/mssql_cdr.sql
rename to contrib/realtime/mssql/mssql_cdr.sql
diff --git a/contrib/realtime/sqlserver/mssql_config.sql b/contrib/realtime/mssql/mssql_config.sql
similarity index 93%
rename from contrib/realtime/sqlserver/mssql_config.sql
rename to contrib/realtime/mssql/mssql_config.sql
index 4834bec..29ca157 100644
--- a/contrib/realtime/sqlserver/mssql_config.sql
+++ b/contrib/realtime/mssql/mssql_config.sql
@@ -982,7 +982,113 @@ ALTER TABLE ps_endpoints ADD CONSTRAINT yesno_values CHECK (media_encryption_opt
 
 GO
 
-INSERT INTO alembic_version (version_num) VALUES ('eb88a14f2a');
+-- Running upgrade eb88a14f2a -> 371a3bf4143e
+
+ALTER TABLE ps_endpoints ADD user_eq_phone VARCHAR(3) NULL;
+
+GO
+
+ALTER TABLE ps_endpoints ADD CONSTRAINT yesno_values CHECK (user_eq_phone IN ('yes', 'no'));
+
+GO
+
+-- Running upgrade 371a3bf4143e -> 45e3f47c6c44
+
+ALTER TABLE ps_globals ADD endpoint_identifier_order VARCHAR(40) NULL;
+
+GO
+
+-- Running upgrade 45e3f47c6c44 -> 23530d604b96
+
+ALTER TABLE ps_endpoints ADD rpid_immediate VARCHAR(3) NULL;
+
+GO
+
+ALTER TABLE ps_endpoints ADD CONSTRAINT yesno_values CHECK (rpid_immediate IN ('yes', 'no'));
+
+GO
+
+-- Running upgrade 23530d604b96 -> 31cd4f4891ec
+
+ALTER TABLE ps_endpoints DROP CONSTRAINT pjsip_dtmf_mode_values;
+
+GO
+
+ALTER TABLE ps_endpoints ALTER COLUMN dtmf_mode VARCHAR(7);
+
+GO
+
+ALTER TABLE ps_endpoints ADD CONSTRAINT pjsip_dtmf_mode_values_v2 CHECK (dtmf_mode IN ('rfc4733', 'inband', 'info', 'auto'));
+
+GO
+
+-- Running upgrade 31cd4f4891ec -> 461d7d691209
+
+ALTER TABLE ps_aors ADD qualify_timeout INTEGER NULL;
+
+GO
+
+ALTER TABLE ps_contacts ADD qualify_timeout INTEGER NULL;
+
+GO
+
+-- Running upgrade 461d7d691209 -> a541e0b5e89
+
+ALTER TABLE ps_globals ADD max_initial_qualify_time INTEGER NULL;
+
+GO
+
+-- Running upgrade a541e0b5e89 -> 28b8e71e541f
+
+ALTER TABLE ps_endpoints ADD g726_non_standard VARCHAR(3) NULL;
+
+GO
+
+ALTER TABLE ps_endpoints ADD CONSTRAINT yesno_values CHECK (g726_non_standard IN ('yes', 'no'));
+
+GO
+
+-- Running upgrade 28b8e71e541f -> 498357a710ae
+
+ALTER TABLE ps_endpoints ADD rtp_keepalive INTEGER NULL;
+
+GO
+
+-- Running upgrade 498357a710ae -> 26f10cadc157
+
+ALTER TABLE ps_endpoints ADD rtp_timeout INTEGER NULL;
+
+GO
+
+ALTER TABLE ps_endpoints ADD rtp_timeout_hold INTEGER NULL;
+
+GO
+
+-- Running upgrade 26f10cadc157 -> 154177371065
+
+ALTER TABLE ps_globals ADD default_from_user VARCHAR(80) NULL;
+
+GO
+
+-- Running upgrade 154177371065 -> 28ce1e718f05
+
+ALTER TABLE ps_registrations ADD fatal_retry_interval INTEGER NULL;
+
+GO
+
+-- Running upgrade 28ce1e718f05 -> 189a235b3fd7
+
+ALTER TABLE ps_globals ADD keep_alive_interval INTEGER NULL;
+
+GO
+
+-- Running upgrade 189a235b3fd7 -> 2d078ec071b7
+
+ALTER TABLE ps_aors ALTER COLUMN contact VARCHAR(255);
+
+GO
+
+INSERT INTO alembic_version (version_num) VALUES ('2d078ec071b7');
 
 GO
 
diff --git a/contrib/realtime/sqlserver/mssql_voicemail.sql b/contrib/realtime/mssql/mssql_voicemail.sql
similarity index 100%
rename from contrib/realtime/sqlserver/mssql_voicemail.sql
rename to contrib/realtime/mssql/mssql_voicemail.sql
diff --git a/contrib/realtime/mysql/mysql_config.sql b/contrib/realtime/mysql/mysql_config.sql
index de713fe..7204f06 100644
--- a/contrib/realtime/mysql/mysql_config.sql
+++ b/contrib/realtime/mysql/mysql_config.sql
@@ -703,3 +703,85 @@ ALTER TABLE ps_endpoints ADD COLUMN media_encryption_optimistic ENUM('yes','no')
 
 UPDATE alembic_version SET version_num='eb88a14f2a';
 
+-- Running upgrade eb88a14f2a -> 371a3bf4143e
+
+ALTER TABLE ps_endpoints ADD COLUMN user_eq_phone ENUM('yes','no');
+
+UPDATE alembic_version SET version_num='371a3bf4143e';
+
+-- Running upgrade 371a3bf4143e -> 45e3f47c6c44
+
+ALTER TABLE ps_globals ADD COLUMN endpoint_identifier_order VARCHAR(40);
+
+UPDATE alembic_version SET version_num='45e3f47c6c44';
+
+-- Running upgrade 45e3f47c6c44 -> 23530d604b96
+
+ALTER TABLE ps_endpoints ADD COLUMN rpid_immediate ENUM('yes','no');
+
+UPDATE alembic_version SET version_num='23530d604b96';
+
+-- Running upgrade 23530d604b96 -> 31cd4f4891ec
+
+ALTER TABLE ps_endpoints CHANGE dtmf_mode dtmf_mode ENUM('rfc4733','inband','info','auto') NULL;
+
+UPDATE alembic_version SET version_num='31cd4f4891ec';
+
+-- Running upgrade 31cd4f4891ec -> 461d7d691209
+
+ALTER TABLE ps_aors ADD COLUMN qualify_timeout INTEGER;
+
+ALTER TABLE ps_contacts ADD COLUMN qualify_timeout INTEGER;
+
+UPDATE alembic_version SET version_num='461d7d691209';
+
+-- Running upgrade 461d7d691209 -> a541e0b5e89
+
+ALTER TABLE ps_globals ADD COLUMN max_initial_qualify_time INTEGER;
+
+UPDATE alembic_version SET version_num='a541e0b5e89';
+
+-- Running upgrade a541e0b5e89 -> 28b8e71e541f
+
+ALTER TABLE ps_endpoints ADD COLUMN g726_non_standard ENUM('yes','no');
+
+UPDATE alembic_version SET version_num='28b8e71e541f';
+
+-- Running upgrade 28b8e71e541f -> 498357a710ae
+
+ALTER TABLE ps_endpoints ADD COLUMN rtp_keepalive INTEGER;
+
+UPDATE alembic_version SET version_num='498357a710ae';
+
+-- Running upgrade 498357a710ae -> 26f10cadc157
+
+ALTER TABLE ps_endpoints ADD COLUMN rtp_timeout INTEGER;
+
+ALTER TABLE ps_endpoints ADD COLUMN rtp_timeout_hold INTEGER;
+
+UPDATE alembic_version SET version_num='26f10cadc157';
+
+-- Running upgrade 26f10cadc157 -> 154177371065
+
+ALTER TABLE ps_globals ADD COLUMN default_from_user VARCHAR(80);
+
+UPDATE alembic_version SET version_num='154177371065';
+
+-- Running upgrade 154177371065 -> 28ce1e718f05
+
+ALTER TABLE ps_registrations ADD COLUMN fatal_retry_interval INTEGER;
+
+UPDATE alembic_version SET version_num='28ce1e718f05';
+
+-- Running upgrade 28ce1e718f05 -> 189a235b3fd7
+
+ALTER TABLE ps_globals ADD COLUMN keep_alive_interval INTEGER;
+
+UPDATE alembic_version SET version_num='189a235b3fd7';
+
+-- Running upgrade 189a235b3fd7 -> 2d078ec071b7
+
+ALTER TABLE ps_aors CHANGE contact contact VARCHAR(255) NULL;
+
+UPDATE alembic_version SET version_num='2d078ec071b7';
+
diff --git a/contrib/realtime/oracle/oracle_config.sql b/contrib/realtime/oracle/oracle_config.sql
index ff6f7d0..f179ca6 100644
--- a/contrib/realtime/oracle/oracle_config.sql
+++ b/contrib/realtime/oracle/oracle_config.sql
@@ -984,7 +984,113 @@ ALTER TABLE ps_endpoints ADD CONSTRAINT yesno_values CHECK (media_encryption_opt
 
 /
 
-INSERT INTO alembic_version (version_num) VALUES ('eb88a14f2a')
+-- Running upgrade eb88a14f2a -> 371a3bf4143e
+
+ALTER TABLE ps_endpoints ADD user_eq_phone VARCHAR(3 CHAR)
+
+/
+
+ALTER TABLE ps_endpoints ADD CONSTRAINT yesno_values CHECK (user_eq_phone IN ('yes', 'no'))
+
+/
+
+-- Running upgrade 371a3bf4143e -> 45e3f47c6c44
+
+ALTER TABLE ps_globals ADD endpoint_identifier_order VARCHAR2(40 CHAR)
+
+/
+
+-- Running upgrade 45e3f47c6c44 -> 23530d604b96
+
+ALTER TABLE ps_endpoints ADD rpid_immediate VARCHAR(3 CHAR)
+
+/
+
+ALTER TABLE ps_endpoints ADD CONSTRAINT yesno_values CHECK (rpid_immediate IN ('yes', 'no'))
+
+/
+
+-- Running upgrade 23530d604b96 -> 31cd4f4891ec
+
+ALTER TABLE ps_endpoints DROP CONSTRAINT pjsip_dtmf_mode_values
+
+/
+
+ALTER TABLE ps_endpoints MODIFY dtmf_mode VARCHAR(7 CHAR)
+
+/
+
+ALTER TABLE ps_endpoints ADD CONSTRAINT pjsip_dtmf_mode_values_v2 CHECK (dtmf_mode IN ('rfc4733', 'inband', 'info', 'auto'))
+
+/
+
+-- Running upgrade 31cd4f4891ec -> 461d7d691209
+
+ALTER TABLE ps_aors ADD qualify_timeout INTEGER
+
+/
+
+ALTER TABLE ps_contacts ADD qualify_timeout INTEGER
+
+/
+
+-- Running upgrade 461d7d691209 -> a541e0b5e89
+
+ALTER TABLE ps_globals ADD max_initial_qualify_time INTEGER
+
+/
+
+-- Running upgrade a541e0b5e89 -> 28b8e71e541f
+
+ALTER TABLE ps_endpoints ADD g726_non_standard VARCHAR(3 CHAR)
+
+/
+
+ALTER TABLE ps_endpoints ADD CONSTRAINT yesno_values CHECK (g726_non_standard IN ('yes', 'no'))
+
+/
+
+-- Running upgrade 28b8e71e541f -> 498357a710ae
+
+ALTER TABLE ps_endpoints ADD rtp_keepalive INTEGER
+
+/
+
+-- Running upgrade 498357a710ae -> 26f10cadc157
+
+ALTER TABLE ps_endpoints ADD rtp_timeout INTEGER
+
+/
+
+ALTER TABLE ps_endpoints ADD rtp_timeout_hold INTEGER
+
+/
+
+-- Running upgrade 26f10cadc157 -> 154177371065
+
+ALTER TABLE ps_globals ADD default_from_user VARCHAR2(80 CHAR)
+
+/
+
+-- Running upgrade 154177371065 -> 28ce1e718f05
+
+ALTER TABLE ps_registrations ADD fatal_retry_interval INTEGER
+
+/
+
+-- Running upgrade 28ce1e718f05 -> 189a235b3fd7
+
+ALTER TABLE ps_globals ADD keep_alive_interval INTEGER
+
+/
+
+-- Running upgrade 189a235b3fd7 -> 2d078ec071b7
+
+ALTER TABLE ps_aors MODIFY contact VARCHAR2(255 CHAR)
+
+/
+
+INSERT INTO alembic_version (version_num) VALUES ('2d078ec071b7')
 
 /
 
diff --git a/contrib/realtime/postgresql/postgresql_config.sql b/contrib/realtime/postgresql/postgresql_config.sql
index cb71ba6..344b2a3 100644
--- a/contrib/realtime/postgresql/postgresql_config.sql
+++ b/contrib/realtime/postgresql/postgresql_config.sql
@@ -733,7 +733,67 @@ DROP TYPE sip_directmedia_values;
 
 ALTER TABLE ps_endpoints ADD COLUMN media_encryption_optimistic yesno_values;
 
-INSERT INTO alembic_version (version_num) VALUES ('eb88a14f2a');
+-- Running upgrade eb88a14f2a -> 371a3bf4143e
+
+ALTER TABLE ps_endpoints ADD COLUMN user_eq_phone yesno_values;
+
+-- Running upgrade 371a3bf4143e -> 45e3f47c6c44
+
+ALTER TABLE ps_globals ADD COLUMN endpoint_identifier_order VARCHAR(40);
+
+-- Running upgrade 45e3f47c6c44 -> 23530d604b96
+
+ALTER TABLE ps_endpoints ADD COLUMN rpid_immediate yesno_values;
+
+-- Running upgrade 23530d604b96 -> 31cd4f4891ec
+
+CREATE TYPE pjsip_dtmf_mode_values_v2 AS ENUM ('rfc4733','inband','info','auto');
+
+ALTER TABLE ps_endpoints ALTER COLUMN dtmf_mode TYPE pjsip_dtmf_mode_values_v2 USING dtmf_mode::text::pjsip_dtmf_mode_values_v2;
+
+DROP TYPE pjsip_dtmf_mode_values;
+
+-- Running upgrade 31cd4f4891ec -> 461d7d691209
+
+ALTER TABLE ps_aors ADD COLUMN qualify_timeout INTEGER;
+
+ALTER TABLE ps_contacts ADD COLUMN qualify_timeout INTEGER;
+
+-- Running upgrade 461d7d691209 -> a541e0b5e89
+
+ALTER TABLE ps_globals ADD COLUMN max_initial_qualify_time INTEGER;
+
+-- Running upgrade a541e0b5e89 -> 28b8e71e541f
+
+ALTER TABLE ps_endpoints ADD COLUMN g726_non_standard yesno_values;
+
+-- Running upgrade 28b8e71e541f -> 498357a710ae
+
+ALTER TABLE ps_endpoints ADD COLUMN rtp_keepalive INTEGER;
+
+-- Running upgrade 498357a710ae -> 26f10cadc157
+
+ALTER TABLE ps_endpoints ADD COLUMN rtp_timeout INTEGER;
+
+ALTER TABLE ps_endpoints ADD COLUMN rtp_timeout_hold INTEGER;
+
+-- Running upgrade 26f10cadc157 -> 154177371065
+
+ALTER TABLE ps_globals ADD COLUMN default_from_user VARCHAR(80);
+
+-- Running upgrade 154177371065 -> 28ce1e718f05
+
+ALTER TABLE ps_registrations ADD COLUMN fatal_retry_interval INTEGER;
+
+-- Running upgrade 28ce1e718f05 -> 189a235b3fd7
+
+ALTER TABLE ps_globals ADD COLUMN keep_alive_interval INTEGER;
+
+-- Running upgrade 189a235b3fd7 -> 2d078ec071b7
+
+ALTER TABLE ps_aors ALTER COLUMN contact TYPE VARCHAR(255);
+
+INSERT INTO alembic_version (version_num) VALUES ('2d078ec071b7');
 
 COMMIT;
 
diff --git a/contrib/scripts/ast_grab_core b/contrib/scripts/ast_grab_core
index a4e62e9..bc56b61 100644
--- a/contrib/scripts/ast_grab_core
+++ b/contrib/scripts/ast_grab_core
@@ -1,5 +1,5 @@
 #!/bin/sh
-# $Id: ast_grab_core 197824 2009-05-28 21:50:27Z seanbright $
+# $Id$
 # lame quickie script to snarf a core of a hung asterisk process.
 # bugs to ast_grab_core, blinky-lights.org  (derrick daugherty)
 
diff --git a/contrib/scripts/astversion b/contrib/scripts/astversion
new file mode 100755
index 0000000..a17c696
--- /dev/null
+++ b/contrib/scripts/astversion
@@ -0,0 +1,536 @@
+#!/bin/bash
+#
+# astversion - determine version/source of components
+#
+# use: astverion {options}
+#
+# options:
+#         --prefix=PATH  - specify prefix from build
+#         --exec=PATH    - specify asterisk executable
+#         --lib=PATH     - specify asterisk library path
+#         --src=PATH     - specify source path to search
+#
+# Copyright (c) 2015, Digium, Inc.
+#
+# Written by Scott Griepentrog <sgriepentrog at digium.com>
+#
+# Distributed under the terms of the GNU General Public License
+
+# condense list of files when more than X in a set
+CONDENSE=3
+
+# libraries to provide the source/version of
+LIBRARIES=(
+	libasteriskssl.so.1
+	libspandsp.so.2
+	libpjsip.so.2
+	libpri.so.1.4
+)
+
+# possible library locations
+LIB_PATHS=(
+	/usr/lib
+	/usr/lib64
+	/lib
+	/lib64
+	/usr/local/lib
+	/usr/local/lib64
+	/opt/lib
+	/opt/lib64
+)
+
+# collection of files to search for
+FILES=()
+
+# source directories to search
+SRC_DIRS=()
+
+main()
+{
+	TMPFILE="/tmp/astversion.$$"
+
+	sanity_check
+	locate_files "$@"
+	locate_libraries
+	locate_modules
+
+	echo "Checking Asterisk versions on $HOSTNAME at $(date)"
+	check_asterisk_version
+	check_dahdi_version
+
+	gather_packages
+	if [ ! -z "$DISTRO" ]
+	then
+		search_packages
+	else
+		echo "WARNING: Unable to determine distro, skipping package search"
+	fi
+	search_source
+	show_unknown_files
+
+	rm -f $TMPFILE
+}
+
+sanity_check()
+{
+	# insure that needed tools are present
+	TOOLS=(uname basename fgrep cut head readlink find)
+	# making assumption that rpm and dpkg always exist on their platforms
+
+	for TOOL in ${TOOLS[@]}
+	do
+		if ! which $TOOL > /dev/null
+		then
+			echo "ERROR: please install package for $TOOL"
+			exit 1
+		fi
+	done
+}
+
+locate_files()
+{
+	# guess prefix from executable path
+	SCRIPT_PREFIX="$(readlink -f ${0%/sbin/astversion} 2>/dev/null)"
+	if [ -x $SCRIPT_PREFIX/sbin/asterisk ]
+	then
+		PREFIX=$SCRIPT_PREFIX
+		ASTERISK_PATH=$SCRIPT_PREFIX/sbin/asterisk
+	fi
+	if [ -z "$ASTERISK_PATH" ]
+	then
+		ASTERISK_PATH=$(readlink -f $(which asterisk 2>/dev/null) 2>/dev/null)
+		PREFIX=${ASTERISK_PATH%/sbin/asterisk}
+	fi
+
+	# parse user supplied information
+	USER_PREFIX=""
+	USER_EXEC=""
+	for opt in "$@"
+	do
+		case "$opt" in
+		-h|--help)
+			echo "Use: astversion {--prefix=PATH} {--exec=PATH} {--lib=PATH}"
+			exit 0
+			;;
+		--prefix=*)
+			USER_PREFIX=${opt:9}
+			;;
+		--exec=*)
+			USER_EXEC=${opt:7}
+			;;
+		--lib=*)
+			LIBDIR=${opt:6}
+			;;
+		--src=*)
+			SRC_DIRS+=${opt:6}
+			;;
+		*)
+			echo "ERROR: Unknown option: $opt"
+			exit 1
+			;;
+		esac
+	done
+
+	# apply user supplied values
+	if [ ! -z "$USER_PREFIX" ]
+	then
+		PREFIX="$USER_PREFIX"
+		ASTERISK_PATH=""
+	fi
+	if [ ! -z "$USER_EXEC" ]
+	then
+		ASTERISK_PATH="$USER_EXEC"
+	fi
+
+	# locate asterisk executable
+	if [ -z "$ASTERISK_PATH" ]
+	then
+		ASTERISK_PATH="$PREFIX/sbin/asterisk"
+	fi
+	if [ ! -x "$ASTERISK_PATH" ]
+	then
+		echo "ERROR: the Asterisk executable is not found or not executable at $ASTERISK_PATH"
+		exit 1
+	fi
+	FILES+=($ASTERISK_PATH)
+
+	# locate dahdi_cfg executable
+	DAHDI_CFG_PATH=$(readlink -f $(which dahdi_cfg 2>/dev/null) 2>/dev/null)
+	if [ ! -z "$DAHDI_CFG_PATH" ]
+	then
+		FILES+=($DAHDI_CFG_PATH)
+	fi
+
+	# locate asterisk libdir
+	if [ -z "$LIBDIR" ]
+	then
+		LIBDIR="$PREFIX/lib"
+		if [ `uname -m` = "x86_64" -a -d "$PREFIX/lib64" ]
+		then
+			LIBDIR="$PREFIX/lib64"
+		fi
+	fi
+
+	if [ ! -d "$LIBDIR/asterisk/modules" ]
+	then
+		echo "ERROR: asterisk module directory not found at $LIBDIR"
+		exit 1
+	fi
+}
+
+locate_libraries()
+{
+	# LIBDIR should contain libasteriskssl, but others may be elsewhere
+
+	# add LIBDIR to path list
+	if ! [[ " ${LIB_PATHS[@]} " =~ " $LIBDIR " ]]
+	then
+		LIB_PATHS+=($LIBDIR)
+	fi
+
+	for LIBRARY in ${LIBRARIES[@]}
+	do
+		FOUND_LIB=()
+		for LIB_PATH in ${LIB_PATHS[@]}
+		do
+			FULL_PATH="$LIB_PATH/$LIBRARY"
+			if [ ! -L $LIB_PATH -a -f $FULL_PATH ]
+			then
+				FOUND_LIB+=($FULL_PATH)
+				FILES+=($FULL_PATH)
+			fi
+		done
+
+		if [ ${#FOUND_LIB[@]} -gt 1 ]
+		then
+			echo "### WARNING: duplicate libraries found: ${FOUND_LIB[@]}"
+		fi
+	done
+}
+
+check_asterisk_version()
+{
+	# get the version that the executable says it is
+	echo "Using Asterisk executable: $ASTERISK_PATH"
+	AST_EXEC_VER=$($ASTERISK_PATH -V)
+	if [ -z "$AST_EXEC_VER" ]
+	then
+		echo "### ERROR: Unable to find Asterisk version from executable"
+		exit 1
+	fi
+	if [ "${AST_EXEC_VER:0:9}" != "Asterisk " ]
+	then
+		echo "### ERROR: Unexpected version from executable: $AST_EXEC_VER"
+		exit 1
+	fi
+
+	# compare with the version that is running
+	if ! $ASTERISK_PATH -rx "core show version" > $TMPFILE 2>/dev/null
+	then
+		echo "Installed version: $AST_EXEC_VER"
+		echo "Asterisk is not running - more details are available when running."
+		AST_RUN_VER=""
+	else
+		AST_RUN_VER=$(grep '^Asterisk [^e][^n][^d]' < $TMPFILE)
+		if [ -z "$AST_RUN_VER" ]
+		then
+			echo "### ERROR: Unable to find Asterisk version from running instance"
+			exit 1
+		fi
+
+		# is it running the same version? (note: space is significant!)
+		if ! fgrep "$AST_EXEC_VER " < $TMPFILE > /dev/null
+		then
+			echo "Installed version: $AST_EXEC_VER"
+			echo "### WARNING: Asterisk is running different version:"
+		fi
+		echo "$AST_RUN_VER"
+	fi
+}
+
+check_dahdi_version()
+{
+	if [ ! -f /sys/module/dahdi/version ]
+	then
+		echo "Dahdi kernel module is not installed"
+	else
+		DAHDI_KERNEL=$(cat /sys/module/dahdi/version)
+		echo "Dahdi kernel module version: $DAHDI_KERNEL"
+	fi
+
+	if ! which dahdi_cfg >&/dev/null
+	then
+		echo "Dahdi tools are not installed"
+	else
+		DAHDI_TOOLS=$(dahdi_cfg -v |& head -1)
+		echo "$DAHDI_TOOLS"
+	fi
+
+	if $ASTERISK_PATH -rx "dahdi show version" > $TMPFILE 2>/dev/null
+	then
+		DAHDI_CLI=$(grep ^DAHDI $TMPFILE)
+		# may be empty if dahdi not installed
+		if [ ! -z "$DAHDI_CLI" ]
+		then
+			echo "Asterisk reports: $DAHDI_CLI"
+		else
+			echo "Asterisk reports that Dahdi is not available"
+		fi
+	fi
+}
+
+scan_package_redhat()
+{
+	PKGNAME="$1"
+
+	if ! rpm -q $PKGNAME > /tmp/astversion-$PKGNAME-version
+	then
+		rm -f /tmp/astversion-$PKGNAME-version
+		return 2
+	fi
+
+	rpm -ql $PKGNAME > /tmp/astversion-$PKGNAME-files
+	rpm -V $PKGNAME > /tmp/astversion-$PKGNAME-verify
+	return 0
+}
+
+scan_package_debian()
+{
+	PKGNAME="$1"
+
+	if ! dpkg -s $PKGNAME > $TMPFILE
+	then
+		rm -f /tmp/astversion-$PKGNAME-version
+		return 2
+	fi
+
+	# prefix the version with the package name to mimic rpm
+	echo -n "$PKGNAME " > /tmp/astversion-$PKGNAME-version
+	cat $TMPFILE | fgrep Version |cut -d ' ' -f2 >> /tmp/astversion-$PKGNAME-version
+
+	dpkg -L $PKGNAME > /tmp/astversion-$PKGNAME-files
+	dpkg -V $PKGNAME > /tmp/astversion-$PKGNAME-verify
+}
+
+package_has_file()
+{
+	PKGNAME="$1"
+	PKGFILE="$2"
+
+	if [ ! -f /tmp/astversion-$PKGNAME-version ]
+	then
+		return 1
+	fi
+
+	if [ ! -f /tmp/astversion-$PKGNAME-files ]
+	then
+		return 2
+	fi
+
+	if ! fgrep "$PKGFILE" /tmp/astversion-$PKGNAME-files >/dev/null
+	then
+		# package doesn't have that file
+		return 3
+	fi
+
+	if fgrep "$PKGFILE" /tmp/astversion-$PKGNAME-verify >/dev/null
+	then
+		# file does not match package
+		return 4
+	fi
+
+	return 0
+}
+
+
+gather_packages()
+{
+	# build a list of installed packages that are likely to contain files of interest
+	PACKAGES=()
+	SEARCH=(asterisk dahdi libpri pjproject spandsp)
+	DISTRO=""
+
+	if [ -f /etc/redhat-release ]
+	then
+		DISTRO="redhat"
+		for NAME in ${SEARCH[@]}
+		do
+			PACKAGES+=($(rpm -qa |fgrep $NAME))
+		done
+	fi
+
+	if [ -f /etc/debian_version ]
+	then
+		DISTRO="debian"
+		for NAME in ${SEARCH[@]}
+		do
+			PACKAGES+=($(dpkg --get-selections |cut -f1 |fgrep $NAME))
+		done
+	fi
+}
+
+locate_modules()
+{
+	# build a list of files that need to be located
+	MODULES=($LIBDIR/asterisk/modules/*.so)
+
+	# add libraries and binaries that exist to the files list
+	for MODULE in ${MODULES[@]}
+	do
+		FILES+=($MODULE)
+	done
+}
+
+search_packages()
+{
+	# search each package and report files that match
+	for PACKAGE in ${PACKAGES[@]}
+	do
+		scan_package_$DISTRO "$PACKAGE"
+		PKGVERSION=$(cat /tmp/astversion-$PKGNAME-version)
+
+		FOUND=()
+		for FILE in ${FILES[@]}
+		do
+			if package_has_file "$PACKAGE" "$FILE"
+			then
+				FOUND+=($FILE)
+				FILES=(${FILES[@]/$FILE/})
+			fi
+		done
+
+		if [ ! -z "$FOUND" ]
+		then
+			if [ ${#FOUND[@]} -le $CONDENSE ]
+			then
+				for FILEFOUND in ${FOUND[@]}
+				do
+					echo "Matched $FILEFOUND to package $PKGVERSION"
+				done
+			else
+				echo "Matched ${#FOUND[@]} files to package $PKGVERSION"
+			fi
+		fi
+
+		rm -f /tmp/astversion-$PKGNAME-version
+		rm -f /tmp/astversion-$PKGNAME-files
+		rm -f /tmp/astversion-$PKGNAME-verify
+	done
+}
+
+search_source()
+{
+	# look for source path locally (compiled on this machine)
+	# - scan elfs for compilation directory
+	# - compare the file to confirm match
+	if [ -z "$FILES" ]
+	then
+		return
+	fi
+
+	# skip this check when without readelf tool (fedora 22)
+	if ! which readelf >& /dev/null
+	then
+		echo "Warning: skipping source detection because readelf utility is not available"
+		return
+	fi
+
+	# build a list of source paths
+	DIRS=()
+	for FILE in ${FILES[@]}
+	do
+		DEBUG_ELF=$(readelf -wi $FILE |fgrep DW_AT_comp_dir |head -1)
+		COMP_DIR=${DEBUG_ELF##* }
+		DIR=${COMP_DIR//[[:space:]]/}
+		if [ -d $DIR ]
+		then
+			if ! [[ " ${DIRS[@]} " =~ " $DIR " ]]
+			then
+				DIRS+=($DIR)
+			fi
+		fi
+	done
+
+	# add in user specified directories last
+	for DIR in ${SRC_DIRS[@]}
+	do
+		if ! [[ " ${DIRS[@]} " =~ " $DIR " ]]
+		then
+			DIRS+=($DIR)
+		fi
+	done
+
+	# for each source path, look for target file
+	for DIR in ${DIRS[@]}
+	do
+		FOUND=()
+		for FILE in ${FILES[@]}
+		do
+			BINARY_FILE=$(basename $FILE)
+			BINARY_PATH="$DIR/$BINARY_FILE"
+			if [ ! -f "$BINARY_PATH" ]
+			then
+				# it may be hiding somewhere
+				FIND_BINARY=$(find $DIR -name $BINARY_FILE |head -1)
+				if [ ! -z "$FIND_BINARY" ]
+				then
+					BINARY_PATH=$FIND_BINARY
+				fi
+			fi
+			if [ -f "$BINARY_PATH" ]
+			then
+				if cmp $BINARY_PATH $FILE >/dev/null
+				then
+					FOUND+=($FILE)
+					FILES=(${FILES[@]/$FILE/})
+				fi
+			fi
+		done
+
+		if [ ! -z "$FOUND" ]
+		then
+			if [ ${#FOUND[@]} -le $CONDENSE ]
+			then
+				for FILEFOUND in ${FOUND[@]}
+				do
+					echo "Located $FILEFOUND compiled from $DIR"
+				done
+			else
+				echo "Located ${#FOUND[@]} files compiled from $DIR"
+			fi
+		fi
+	done
+}
+
+show_unknown_files()
+{
+	# show a warning for any remaining files unaccounted for
+	if [ -z "$FILES" ]
+	then
+		echo "Success: all files accounted for."
+	else
+		echo ""
+		echo "WARNING: source of the following files was not found:"
+		if ! which readelf >& /dev/null
+		then
+			for FILE in ${FILES[@]}
+			do
+				echo "     ### $FILE"
+			done
+		else
+			for FILE in ${FILES[@]}
+			do
+				DEBUG_ELF=$(readelf -wi $FILE |fgrep DW_AT_comp_dir |head -1)
+				if [ -z "$DEBUG_ELF" ]
+				then
+					COMP_DIR="(no debug info)"
+				else
+					COMP_DIR=${DEBUG_ELF##* }
+				fi
+				echo "     ### $FILE - $COMP_DIR"
+			done
+		fi
+	fi
+}
+
+main "$@"
diff --git a/contrib/scripts/autosupport b/contrib/scripts/autosupport
index 4d5ab59..e41215f 100644
--- a/contrib/scripts/autosupport
+++ b/contrib/scripts/autosupport
@@ -74,7 +74,7 @@ if [ $MYUID -ne  0 ]; then
 fi
 
 SUPPORTED_MODULES="wcb4xxp wct4xxp wctc4xxp wctdm wctdm24xxp wcte11xp wcte12xp"
-RELATED_MODULES="$SUPPORTED_MODULES dahdi_dummy dahdi_transcode dahdi_vpmadt032_loader zaptel ztdummy zttranscode";
+RELATED_MODULES="$SUPPORTED_MODULES dahdi_dummy dahdi_transcode dahdi_vpmadt032_loader";
 
 OUTPUT_FILE="${FILE_PREFIX}${OUTPUT_FILE}"
 TARBALL_OUTPUT_FILE="${FILE_PREFIX}${TARBALL_OUTPUT_FILE}"
@@ -152,18 +152,16 @@ echo >> $OUTPUT;
 echo >> $OUTPUT;
 echo -n "."
 
-# Check for loaded Zaptel/DAHDI modules
-for module in dahdi zaptel; do
-  if [ -d /sys/module/$module ]; then
-    echo "------------------" >> $OUTPUT
-    echo "$module version:" >> $OUTPUT
-    echo "------------------" >> $OUTPUT
-    echo "/sys/module/$module/version: " >> $OUTPUT
-    cat /sys/module/$module/version 2> /dev/null >> $OUTPUT
-    echo "" >> $OUTPUT;
-    echo -n "."
-  fi
-done
+
+if [ -d /sys/module/dahdi ]; then
+  echo "------------------" >> $OUTPUT
+  echo "dahdi version:" >> $OUTPUT
+  echo "------------------" >> $OUTPUT
+  echo "/sys/module/dahdi/version: " >> $OUTPUT
+  cat /sys/module/dahdi/version 2> /dev/null >> $OUTPUT
+  echo "" >> $OUTPUT;
+  echo -n "."
+fi
 
 echo "------------------" >> $OUTPUT;
 echo "DAHDI TOOLS : dahdi_cfg --help" >> $OUTPUT;
@@ -188,13 +186,16 @@ asterisk -V >> $OUTPUT;
 echo >> $OUTPUT;
 # Add check to see if asterisk is running.
 if [ -e /var/run/asterisk.ctl ] || [ -e /var/run/asterisk/asterisk.ctl ]; then
-  for command in "show version" "core show version" "pri show version" "dahdi show version" "transcoder show" \
-      "core show uptime" "pri show spans" "misdn show stacks" "zap show channels" "dahdi show status" "dahdi show channels" \
-      "dahdi show channel 1" "core show channels" "skype show version" "skype show licenses" "skype show users" \
-      "skype show hostid" "show g729" "g729 show version" "g729 show licenses" "g729 show hostid" "fax show version" \
-      "fax show licenses" "fax show hostid" "fax show stats" "digium_phones show version" "digium_phones show alerts" \
-			"digium_phones show applications" "digium_phones show firmwares" "digium_phones show lines" "digium_phones show networks" \
-			"digium_phones show phones" "digium_phones show sessions" "digium_phones show settings" "digium_phones show translations" ;
+  for command in "core show version" "pri show version" "dahdi show version" "core show translation" \
+      "core show uptime" "core show settings" "core show sysinfo" "core show channels" \
+      "pri show spans" "dahdi show status" "dahdi show channels" "dahdi show channel 1" \
+      "pjsip show endpoints" "pjsip show registrations" "pjsip list channels" \
+      "sip show peers" "sip show registry" "sip show channels" "sip show subscriptions" "sip show settings" \
+      "show g729" "g729 show version" "g729 show licenses" "g729 show hostid" \
+      "digium_phones show version" "digium_phones show alerts" "digium_phones show applications" \
+      "digium_phones show firmwares" "digium_phones show lines" "digium_phones show networks" \
+			"digium_phones show phones" "digium_phones show sessions" "digium_phones show settings" \
+      "digium_phones show translations" ;
 	do
     echo "asterisk -rx \"$command\"" >> $OUTPUT;
     asterisk -rx "$command" >> $OUTPUT;
@@ -317,19 +318,17 @@ echo >> $OUTPUT;
 echo -n "."
 
 echo "----------------------------" >> $OUTPUT;
-echo "CAT OF DAHDI/ZAPTEL CHANNELS : cat /proc/dahdi/" >> $OUTPUT;
+echo "CAT OF DAHDI CHANNELS : cat /proc/dahdi/" >> $OUTPUT;
 echo "----------------------------" >> $OUTPUT;
-for tech in dahdi zaptel zap; do
-  if [ -d /proc/$tech/ ]; then
-    for file in $(ls /proc/$tech/ 2> /dev/null); do
-      echo "----------------------------" >> $OUTPUT;
-      echo "/proc/$tech/$file:" >> $OUTPUT;
-      cat /proc/$tech/$file >> $OUTPUT;
-      echo >> $OUTPUT;
-      echo -n "."
-    done
-  fi
-done
+if [ -d /proc/dahdi/ ]; then
+  for file in $(ls /proc/dahdi/ 2> /dev/null); do
+    echo "----------------------------" >> $OUTPUT;
+    echo "/proc/dahdi/$file:" >> $OUTPUT;
+    cat /proc/dahdi/$file >> $OUTPUT;
+    echo >> $OUTPUT;
+    echo -n "."
+  done
+fi
 echo >> $OUTPUT;
 
 echo "------------------" >> $OUTPUT;
@@ -371,7 +370,7 @@ for mod in $SUPPORTED_MODULES; do
 done
 
 echo "------------------------" >> $OUTPUT;
-echo "DAHDI/ZAPTEL MODULE INFO : modinfo" >> $OUTPUT;
+echo "DAHDI MODULE INFO : modinfo" >> $OUTPUT;
 echo "------------------------" >> $OUTPUT;
 for file in $(ls /lib/modules/$(uname -r)/dahdi/*.ko \
   /lib/modules/$(uname -r)/dahdi/*/*.ko\
@@ -518,26 +517,9 @@ collect_config_backup()
     TAR_FILES="$TAR_FILES /etc/dahdi*"
   fi
 
-  # Check for Zaptel Module configuration
-  if [ -f /etc/sysconfig/zaptel ]; then
-    TAR_FILES="$TAR_FILES /etc/sysconfig/zaptel*"
-  fi
-
-  # Check for Zaptel Module configuration (alternate location)
-  if [ -f /etc/default/zaptel ]; then
-    TAR_FILES="$TAR_FILES /etc/default/zaptel*"
-  fi
-
-  # Grab the dahdi/zaptel init scripts, in case they have been modified
-  for driver in dahdi zaptel; do
-    if [ -f /etc/init.d/$driver ]; then
-      TAR_FILES="$TAR_FILES /etc/init.d/$driver"
-    fi
-  done
-
-  # Check for zaptel.conf
-  if [ -f /etc/zaptel.conf ]; then
-    TAR_FILES="$TAR_FILES /etc/zaptel*"
+  # Grab the dahdi init scripts, in case they have been modified
+  if [ -f /etc/init.d/dahdi ]; then
+    TAR_FILES="$TAR_FILES /etc/init.d/dahdi"
   fi
 
   # Check for fxotune.conf
@@ -567,6 +549,9 @@ collect_config_backup()
   if [ -f /var/log/messages ]; then
     TAR_FILES="$TAR_FILES /var/log/messages"
   fi
+  if [ -f /var/log/syslog ]; then
+    TAR_FILES="$TAR_FILES /var/log/syslog"
+  fi
 
   [ -f $TARBALL_OUTPUT ] && rm -rf $TARBALL_OUTPUT
   [ -f $TARBALL_OUTPUT.gz ] && rm -rf $TARBALL_OUTPUT.gz
@@ -616,8 +601,8 @@ else
   echo "pci listing, dmesg, running processes, and kernel version"
   echo
   echo "2. A backup of elements of your configuration such as:"
-  echo "asterisk config files, license files, loaded dahdi/zaptel module"
-  echo "parameters, and other asterisk/dahdi/zaptel related files."
+  echo "asterisk config files, license files, loaded dahdi module"
+  echo "parameters, and other asterisk/dahdi related files."
   echo
   echo "Collect this information [y/n] ? "
   read files;
diff --git a/contrib/scripts/clang-scan-build b/contrib/scripts/clang-scan-build
new file mode 100755
index 0000000..c5f93ac
--- /dev/null
+++ b/contrib/scripts/clang-scan-build
@@ -0,0 +1,136 @@
+#!/bin/bash
+#
+# clang-scan-build: configure and compile asterisk using the llvm static analyzer
+
+# Options/Flags:
+# -c|--compiler	: either [clang|gcc]
+# --cflags	: cflags you would like to add to the default set
+# --configure	: configure flags you would like to use instead off the default set
+# --make	: make flags you would like to use instead off the default set
+# --scanbuild	: scanbuild flags you would like to use instead of the default set
+# --outputdir	: directory where scan-build should create the html files
+# -h|--help	: this help
+
+# Usage:
+# contrib/scripts/clang-scan-build
+# This script will use clang if available and no compiler has been specified
+#
+# Example usage:
+#
+#   contrib/scripts/clang-scan-build
+#   contrib/scripts/clang-scan-build -c gcc
+#   contrib/scripts/clang-scan-build --compiler clang --configure "--enable-dev-mode" --outputdir="/tmp/scan-build_output"
+#   contrib/scripts/clang-scan-build --make "-j2"
+#
+# scan-build will generate html files during the make process, which will be stored in the specified outputdir or ./scan-build_output" by default
+
+# Copyright (C) 2015 Diederik de Groot <dddegroot at users.sf.net>
+#
+# 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
+
+COMPILER=clang
+SCANBUILD="`which scan-build`"
+CFLAGS=""
+CONFIGURE_FLAGS="--enable-coverage --disable-xmldoc"
+MAKE_FLAGS=""
+SCANBUILD_FLAGS="-maxloop 10 -disable-checker deadcode.DeadStores -enable-checker alpha.core.BoolAssignment -enable-checker alpha.core.CallAndMessageUnInitRefArg -enable-checker alpha.core.CastSize -enable-checker alpha.core.CastToStruct -enable-checker alpha.core.IdenticalExpr -enable-checker alpha.core.PointerArithm -enable-checker alpha.core.PointerSub -enable-checker alpha.core.SizeofPtr -enable-checker alpha.core.TestAfterDivZero -enable-checker alpha.security.ArrayBound -enable-che [...]
+OUTPUTDIR="scan-build_output"
+
+function print_usage {
+cat << EOF
+$0 Usage:
+
+Options/Flags:
+-c|--compiler	: either [clang|gcc]
+--cflags	: cflags you would like to add to the default set:
+		  "${CFLAGS}"
+
+--configure	: configure flags you would like to use instead off the default set:
+		  "${CONFIGURE_FLAGS}"
+
+--make		: make flags you would like to use instead off the default set:
+		  "${MAKE_FLAGS}"
+
+--scanbuild	: scanbuild flags you would like to use instead of the default set:
+		  "${SCANBUILD_FLAGS}"
+
+--outputdir	: directory where scan-build should create the html files. default:
+		  "`pwd`/${OUTPUTDIR}"
+
+-h|--help	: this help
+EOF
+}
+
+for i in "$@"
+do
+	case $i in
+		-c=*|--compiler=*)
+			COMPILER="${i#*=}"
+			shift
+		;;
+		--cflags=*)
+			CFLAGS="${i#*=}"
+			shift
+		;;
+		--configure=*)
+			CONFIGURE_FLAGS="${i#*=}"
+			shift
+		;;
+		--make=*)
+			MAKE_FLAGS="${i#*=}"
+			shift
+		;;
+		--scanbuild=*)
+			SCANBUILD_FLAGS="${i#*=}"
+			shift
+		;;
+		--outputdir=*)
+			OUTPUTDIR="${i#*=}"
+			shift
+		;;
+		-h|--help)
+			print_usage
+			exit
+		;;
+	esac
+done
+
+if [ "${COMPILER}" == "clang" ] && [ ! -z "`which clang`" ]; then
+	CCC_CC="`which`clang"
+	CCC_CXX="`which clang++`"
+	CFLAGS="-fblocks ${CFLAGS}"
+elif [ "${COMPILER}" == "gcc" ] && [ ! -z "`which gcc`" ]; then
+	CCC_CC="`which gcc`"
+	CCC_CXX="`which g++`"
+	CFLAGS="${CFLAGS}"
+else
+	echo "Unknown compiler: $2, needs to be either clang or gcc"
+	exit
+fi
+
+if [ ! -f config.status ]; then
+	echo "Running ./configure ${CONFIGURE_FLAGS} ..."
+	${SCANBUILD} ${SCANBUILD_FLAGS} -o ${OUTPUTDIR} ./configure ${CONFIGURE_FLAGS}
+	if [ $? != 0 ]; then
+		echo "Configure error occurred, see output / config.log"
+		exit
+	fi
+	make clean
+fi
+if [ -f config.status ]; then
+	echo "Running scan-build make ${MAKE_FLAGS} ..."
+	${SCANBUILD} ${SCANBUILD_FLAGS} -o ${OUTPUTDIR} make ${MAKE_FLAGS}
+fi
diff --git a/contrib/scripts/dbsep.cgi b/contrib/scripts/dbsep.cgi
index 4e80479..d15390c 100755
--- a/contrib/scripts/dbsep.cgi
+++ b/contrib/scripts/dbsep.cgi
@@ -14,7 +14,7 @@
 # the GNU General Public License Version 2. See the LICENSE file
 # at the top of the source tree.
 #
-# $Id: dbsep.cgi 253758 2010-03-22 19:05:27Z tilghman $
+# $Id$
 #
 
 use CGI;
diff --git a/contrib/scripts/install_prereq b/contrib/scripts/install_prereq
index a6127a9..4639478 100755
--- a/contrib/scripts/install_prereq
+++ b/contrib/scripts/install_prereq
@@ -1,6 +1,6 @@
 #! /bin/sh
 #
-# $Id: install_prereq 395985 2013-08-01 17:07:52Z kmoore $
+# $Id$
 #
 
 # install_prereq: a script to install distribution-specific
@@ -10,7 +10,7 @@ set -e
 
 usage() {
 	echo "$0: a script to install distribution-specific prerequirement"
-	echo 'Revision: $Id: install_prereq 395985 2013-08-01 17:07:52Z kmoore $'
+	echo 'Revision: $Id$'
 	echo ""
 	echo "Usage: $0:                    Shows this message."
 	echo "Usage: $0 test                Prints commands it is about to run."
@@ -31,7 +31,7 @@ PACKAGES_DEBIAN="$PACKAGES_DEBIAN libresample-dev libc-client-dev binutils-dev l
 PACKAGES_DEBIAN="$PACKAGES_DEBIAN subversion git libxslt1-dev"
 PACKAGES_RH="automake gcc gcc-c++ ncurses-devel openssl-devel libxml2-devel unixODBC-devel libcurl-devel libogg-devel libvorbis-devel speex-devel"
 PACKAGES_RH="$PACKAGES_RH spandsp-devel freetds-devel net-snmp-devel iksemel-devel corosynclib-devel newt-devel popt-devel libtool-ltdl-devel lua-devel"
-PACKAGES_RH="$PACKAGES_RH libsqlite3x-devel radiusclient-ng-devel portaudio-devel postgresql-devel libresample-devel neon-devel libical-devel"
+PACKAGES_RH="$PACKAGES_RH sqlite-devel libsqlite3x-devel radiusclient-ng-devel portaudio-devel postgresql-devel libresample-devel neon-devel libical-devel"
 PACKAGES_RH="$PACKAGES_RH openldap-devel gmime22-devel sqlite2-devel mysql-devel bluez-libs-devel jack-audio-connection-kit-devel gsm-devel libedit-devel libuuid-devel"
 PACKAGES_RH="$PACKAGES_RH jansson-devel libsrtp-devel pjproject-devel subversion git libxslt-devel"
 
@@ -71,7 +71,8 @@ check_installed_debs() {
 		tocheck="${tocheck} ^${pack}$"
 	done
 	aptitude -F '%c %p' search ${tocheck} 2>/dev/null \
- 	| awk '/^p/{print $2}'
+		| awk '/^p/{print $2}' \
+		| grep -v ':i386$'
 }
 
 # parsing the output of yum is close to impossible.
@@ -162,7 +163,7 @@ install_unpackaged() {
 			cd pjproject
 			git pull
 		fi
-		./configure --enable-shared --with-external-speex --with-external-gsm --with-external-srtp --disable-sound --disable-resample && make && make install
+		./configure CFLAGS="-DNDEBUG -DPJ_HAS_IPV6=1" --enable-shared --with-external-speex --with-external-gsm --with-external-srtp --disable-sound --disable-resample && make && make install
 		cd ..
 		echo "/usr/local/lib" > /etc/ld.so.conf.d/usr_local.conf
 		/sbin/ldconfig
diff --git a/contrib/scripts/retrieve_extensions_from_sql.pl b/contrib/scripts/retrieve_extensions_from_sql.pl
index 848dbec..cf17d03 100644
--- a/contrib/scripts/retrieve_extensions_from_sql.pl
+++ b/contrib/scripts/retrieve_extensions_from_sql.pl
@@ -16,7 +16,7 @@
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 #
-# $Id: retrieve_extensions_from_sql.pl 9991 2006-02-14 19:14:15Z kpfleming $
+# $Id$
 #
 # Use these commands to create the appropriate SQL tables
 # If flags is 1 then the record is not included in the output extensions file
diff --git a/contrib/scripts/sip_to_pjsip/astconfigparser.py b/contrib/scripts/sip_to_pjsip/astconfigparser.py
index c93173d..b207b0d 100644
--- a/contrib/scripts/sip_to_pjsip/astconfigparser.py
+++ b/contrib/scripts/sip_to_pjsip/astconfigparser.py
@@ -1,4 +1,5 @@
 import re
+import itertools
 
 from astdicts import OrderedDict
 from astdicts import MultiOrderedDict
@@ -331,7 +332,9 @@ class MultiOrderedConfigParser:
         res = sections[key] if key in sections else []
         searched.append(self)
         if self._includes:
-            res += self._includes.get_sections(key, attr, searched)
+            res.extend(list(itertools.chain(*[
+                incl.get_sections(key, attr, searched)
+                for incl in self._includes.itervalues()])))
         if self._parent:
             res += self._parent.get_sections(key, attr, searched)
         return res
@@ -415,15 +418,15 @@ class MultiOrderedConfigParser:
         else:
             self.defaults(section)[0][key] = val
 
-    def read(self, filename):
+    def read(self, filename, sect=None):
         """Parse configuration information from a file"""
         try:
             with open(filename, 'rt') as config_file:
-                self._read(config_file)
+                self._read(config_file, sect)
         except IOError:
             print "Could not open file ", filename, " for reading"
 
-    def _read(self, config_file):
+    def _read(self, config_file, sect):
         """Parse configuration information from the config_file"""
         is_comment = False  # used for multi-lined comments
         for line in config_file:
@@ -435,7 +438,7 @@ class MultiOrderedConfigParser:
             include_name = try_include(line)
             if include_name:
                 parser = self.add_include(include_name)
-                parser.read(include_name)
+                parser.read(include_name, sect)
                 continue
 
             section, is_template, templates = try_section(line)
@@ -447,6 +450,8 @@ class MultiOrderedConfigParser:
                 continue
 
             key, val = try_option(line)
+            if sect is None:
+                raise Exception("Section not defined before assignment")
             sect[key] = val
 
     def write(self, config_file):
diff --git a/contrib/scripts/sip_to_pjsip/sip_to_pjsip.py b/contrib/scripts/sip_to_pjsip/sip_to_pjsip.py
index 8fdef31..8909216 100755
--- a/contrib/scripts/sip_to_pjsip/sip_to_pjsip.py
+++ b/contrib/scripts/sip_to_pjsip/sip_to_pjsip.py
@@ -587,7 +587,11 @@ def create_udp(sip, pjsip, nmapped):
     externhost
     """
 
-    bind = sip.multi_get('general', ['udpbindaddr', 'bindaddr'])[0]
+    try:
+        bind = sip.multi_get('general', ['udpbindaddr', 'bindaddr'])[0]
+    except LookupError:
+        bind = ''
+
     bind = build_host(sip, bind, 'general', 'bindport')
 
     try:
@@ -974,11 +978,12 @@ class Registration:
 
         auth_section = 'auth_reg_' + self.host
 
-        if self.secret:
+        if hasattr(self, 'secret') and self.secret:
             set_value('password', self.secret, auth_section, pjsip, nmapped,
                       'auth')
-            set_value('username', self.authuser or self.user, auth_section,
-                      pjsip, nmapped, 'auth')
+            if hasattr(self, 'authuser'):
+                set_value('username', self.authuser or self.user, auth_section,
+                          pjsip, nmapped, 'auth')
             set_value('outbound_auth', auth_section, section, pjsip, nmapped,
                       'registration')
 
@@ -988,7 +993,7 @@ class Registration:
         else:
             client_uri += self.host
 
-        if self.domainport:
+        if hasattr(self, 'domainport') and self.domainport:
             client_uri += ":" + self.domainport
         elif self.port:
             client_uri += ":" + self.port
@@ -1136,8 +1141,9 @@ def cli_options():
     """
     global PREFIX
     usage = "usage: %prog [options] [input-file [output-file]]\n\n" \
-        "input-file defaults to 'sip.conf'\n" \
-        "output-file defaults to 'pjsip.conf'"
+		"Converts the chan_sip configuration input-file to the chan_pjsip output-file.\n"\
+        "The input-file defaults to 'sip.conf'.\n" \
+        "The output-file defaults to 'pjsip.conf'."
     parser = optparse.OptionParser(usage=usage)
     parser.add_option('-p', '--prefix', dest='prefix', default=PREFIX,
                       help='output prefix for include files')
@@ -1154,6 +1160,9 @@ if __name__ == "__main__":
     sip_filename, pjsip_filename = cli_options()
     # configuration parser for sip.conf
     sip = astconfigparser.MultiOrderedConfigParser()
+    print 'Reading', sip_filename
     sip.read(sip_filename)
+    print 'Converting to PJSIP...'
     pjsip, non_mappings = convert(sip, pjsip_filename, dict(), False)
+    print 'Writing', pjsip_filename
     write_pjsip(pjsip_filename, pjsip, non_mappings)
diff --git a/contrib/utils/eagi_proxy.c b/contrib/utils/eagi_proxy.c
index 03c7e06..0a96d5a 100644
--- a/contrib/utils/eagi_proxy.c
+++ b/contrib/utils/eagi_proxy.c
@@ -223,7 +223,7 @@ int connect_to_host(char* name, int port)
 	struct sockaddr_in host;
 	
 
-	/* get adress */	
+	/* get address */	
 	if(!strcmp(name,"localhost"))
 		address=htonl(2130706433); /*127.0.0.1*/
 	else
diff --git a/doc/appdocsxml.xslt b/doc/appdocsxml.xslt
index 8cbeaa3..6e4c5ac 100644
--- a/doc/appdocsxml.xslt
+++ b/doc/appdocsxml.xslt
@@ -98,6 +98,12 @@
                 <xsl:value-of select="concat(@prefix,'Uniqueid')"/>
             </xsl:attribute>
         </xsl:element>
+        <xsl:element name="parameter">
+            <xsl:attribute name="name">
+                <xsl:value-of select="concat(@prefix,'Linkedid')"/>
+            </xsl:attribute>
+            <para>Uniqueid of the oldest channel associated with this channel.</para>
+        </xsl:element>
     </xsl:template>
 
     <xsl:template match="bridge_snapshot">
diff --git a/contrib/asterisk-ng-doxygen b/doc/asterisk-ng-doxygen.in
similarity index 100%
rename from contrib/asterisk-ng-doxygen
rename to doc/asterisk-ng-doxygen.in
diff --git a/formats/format_g719.c b/formats/format_g719.c
index d18801c..f582862 100644
--- a/formats/format_g719.c
+++ b/formats/format_g719.c
@@ -29,7 +29,7 @@
  
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/mod_format.h"
 #include "asterisk/module.h"
diff --git a/formats/format_g723.c b/formats/format_g723.c
index 540f26a..6d0b963 100644
--- a/formats/format_g723.c
+++ b/formats/format_g723.c
@@ -31,7 +31,7 @@
  
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/mod_format.h"
 #include "asterisk/module.h"
diff --git a/formats/format_g726.c b/formats/format_g726.c
index 740e6d6..25daff9 100644
--- a/formats/format_g726.c
+++ b/formats/format_g726.c
@@ -34,7 +34,7 @@
  
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/mod_format.h"
 #include "asterisk/module.h"
diff --git a/formats/format_g729.c b/formats/format_g729.c
index fc3fadd..2c677b2 100644
--- a/formats/format_g729.c
+++ b/formats/format_g729.c
@@ -32,7 +32,7 @@
  
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/mod_format.h"
 #include "asterisk/module.h"
diff --git a/formats/format_gsm.c b/formats/format_gsm.c
index fb57834..4a688e2 100644
--- a/formats/format_gsm.c
+++ b/formats/format_gsm.c
@@ -29,7 +29,7 @@
  
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/mod_format.h"
 #include "asterisk/module.h"
diff --git a/formats/format_h263.c b/formats/format_h263.c
index d86e833..731b746 100644
--- a/formats/format_h263.c
+++ b/formats/format_h263.c
@@ -30,7 +30,7 @@
  
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/mod_format.h"
 #include "asterisk/module.h"
diff --git a/formats/format_h264.c b/formats/format_h264.c
index 06500ec..7c3d2d4 100644
--- a/formats/format_h264.c
+++ b/formats/format_h264.c
@@ -30,7 +30,7 @@
  
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/mod_format.h"
 #include "asterisk/module.h"
diff --git a/formats/format_ilbc.c b/formats/format_ilbc.c
index c3ef1b0..6b811e6 100644
--- a/formats/format_ilbc.c
+++ b/formats/format_ilbc.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/mod_format.h"
 #include "asterisk/module.h"
diff --git a/formats/format_jpeg.c b/formats/format_jpeg.c
index 775d7ad..5b2c72d 100644
--- a/formats/format_jpeg.c
+++ b/formats/format_jpeg.c
@@ -30,7 +30,7 @@
  
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/mod_format.h"
 #include "asterisk/module.h"
diff --git a/formats/format_ogg_vorbis.c b/formats/format_ogg_vorbis.c
index 69ea694..ed616c5 100644
--- a/formats/format_ogg_vorbis.c
+++ b/formats/format_ogg_vorbis.c
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <vorbis/codec.h>
 #include <vorbis/vorbisenc.h>
diff --git a/formats/format_pcm.c b/formats/format_pcm.c
index fbee94f..beb0015 100644
--- a/formats/format_pcm.c
+++ b/formats/format_pcm.c
@@ -30,7 +30,7 @@
  
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/mod_format.h"
 #include "asterisk/module.h"
diff --git a/formats/format_siren14.c b/formats/format_siren14.c
index 911a420..d0a16c4 100644
--- a/formats/format_siren14.c
+++ b/formats/format_siren14.c
@@ -29,7 +29,7 @@
  
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/mod_format.h"
 #include "asterisk/module.h"
diff --git a/formats/format_siren7.c b/formats/format_siren7.c
index dcf2cd9..acfde42 100644
--- a/formats/format_siren7.c
+++ b/formats/format_siren7.c
@@ -29,7 +29,7 @@
  
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/mod_format.h"
 #include "asterisk/module.h"
diff --git a/formats/format_sln.c b/formats/format_sln.c
index 13833eb..143e4da 100644
--- a/formats/format_sln.c
+++ b/formats/format_sln.c
@@ -27,7 +27,7 @@
  
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/mod_format.h"
 #include "asterisk/module.h"
diff --git a/formats/format_vox.c b/formats/format_vox.c
index 0440885..6724c54 100644
--- a/formats/format_vox.c
+++ b/formats/format_vox.c
@@ -30,7 +30,7 @@
  
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/mod_format.h"
 #include "asterisk/module.h"
diff --git a/formats/format_wav.c b/formats/format_wav.c
index fb2e94c..90ef986 100644
--- a/formats/format_wav.c
+++ b/formats/format_wav.c
@@ -30,7 +30,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/mod_format.h"
 #include "asterisk/module.h"
@@ -64,14 +64,14 @@ struct wav_desc {	/* format-specific parameters */
 #define ltohs(b) (b)
 #else
 #if __BYTE_ORDER == __BIG_ENDIAN
-#define htoll(b)  \
-          (((((b)      ) & 0xFF) << 24) | \
-	       ((((b) >>  8) & 0xFF) << 16) | \
-		   ((((b) >> 16) & 0xFF) <<  8) | \
-		   ((((b) >> 24) & 0xFF)      ))
+#define htoll(b) \
+	(((((b)      ) & 0xFF) << 24) | \
+	((( (b) >>  8) & 0xFF) << 16) | \
+	((( (b) >> 16) & 0xFF) <<  8) | \
+	((( (b) >> 24) & 0xFF)      ))
 #define htols(b) \
-          (((((b)      ) & 0xFF) << 8) | \
-		   ((((b) >> 8) & 0xFF)      ))
+	(((((b)      ) & 0xFF) <<  8) | \
+	((( (b) >>  8) & 0xFF)      ))
 #define ltohl(b) htoll(b)
 #define ltohs(b) htols(b)
 #else
@@ -110,8 +110,8 @@ static int check_header_fmt(FILE *f, int hsize, int hz)
 		return -1;
 	}
 	if (((ltohl(freq) != 8000) && (ltohl(freq) != 16000)) ||
-	    ((ltohl(freq) == 8000) && (hz != 8000)) ||
-	    ((ltohl(freq) == 16000) && (hz != 16000))) {
+		((ltohl(freq) == 8000) && (hz != 8000)) ||
+		((ltohl(freq) == 16000) && (hz != 16000))) {
 		ast_log(LOG_WARNING, "Unexpected frequency mismatch %d (expecting %d)\n", ltohl(freq),hz);
 		return -1;
 	}
@@ -153,7 +153,9 @@ static int check_header(FILE *f, int hz)
 		ast_log(LOG_WARNING, "Read failed (size)\n");
 		return -1;
 	}
+#if __BYTE_ORDER == __BIG_ENDIAN
 	size = ltohl(size);
+#endif
 	if (fread(&formtype, 1, 4, f) != 4) {
 		ast_log(LOG_WARNING, "Read failed (formtype)\n");
 		return -1;
@@ -170,30 +172,32 @@ static int check_header(FILE *f, int hz)
 	for(;;)
 	{ 
 		char buf[4];
-	    
-	    /* Begin data chunk */
-	    if (fread(&buf, 1, 4, f) != 4) {
+		
+		/* Begin data chunk */
+		if (fread(&buf, 1, 4, f) != 4) {
 			ast_log(LOG_WARNING, "Read failed (block header format)\n");
 			return -1;
-	    }
-	    /* Data has the actual length of data in it */
-	    if (fread(&data, 1, 4, f) != 4) {
+		}
+		/* Data has the actual length of data in it */
+		if (fread(&data, 1, 4, f) != 4) {
 			ast_log(LOG_WARNING, "Read failed (block '%.4s' header length)\n", buf);
 			return -1;
-	    }
-	    data = ltohl(data);
+		}
+#if __BYTE_ORDER == __BIG_ENDIAN
+		data = ltohl(data);
+#endif
 		if (memcmp(&buf, "fmt ", 4) == 0) {
 			if (check_header_fmt(f, data, hz))
 				return -1;
 			continue;
 		}
-	    if(memcmp(buf, "data", 4) == 0 ) 
+		if(memcmp(buf, "data", 4) == 0 ) 
 			break;
 		ast_log(LOG_DEBUG, "Skipping unknown block '%.4s'\n", buf);
-	    if (fseek(f,data,SEEK_CUR) == -1 ) {
+		if (fseek(f,data,SEEK_CUR) == -1 ) {
 			ast_log(LOG_WARNING, "Failed to skip '%.4s' block: %d\n", buf, data);
 			return -1;
-	    }
+		}
 	}
 #if 0
 	curpos = lseek(fd, 0, SEEK_CUR);
@@ -461,13 +465,14 @@ static int wav_seek(struct ast_filestream *fs, off_t sample_offset, int whence)
 		return -1;
 	}
 
-	if (whence == SEEK_SET)
+	if (whence == SEEK_SET) {
 		offset = samples + min;
-	else if (whence == SEEK_CUR || whence == SEEK_FORCECUR)
+	} else if (whence == SEEK_CUR || whence == SEEK_FORCECUR) {
 		offset = samples + cur;
-	else if (whence == SEEK_END)
+	} else if (whence == SEEK_END) {
 		offset = max - samples;
-        if (whence != SEEK_FORCECUR) {
+	}
+	if (whence != SEEK_FORCECUR) {
 		offset = (offset > max)?max:offset;
 	}
 	/* always protect the header space. */
diff --git a/formats/format_wav_gsm.c b/formats/format_wav_gsm.c
index 2591055..06597ab 100644
--- a/formats/format_wav_gsm.c
+++ b/formats/format_wav_gsm.c
@@ -33,7 +33,7 @@
  
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/mod_format.h"
 #include "asterisk/module.h"
@@ -74,14 +74,14 @@ struct wavg_desc {
 #define ltohs(b) (b)
 #else
 #if __BYTE_ORDER == __BIG_ENDIAN
-#define htoll(b)  \
-          (((((b)      ) & 0xFF) << 24) | \
-	       ((((b) >>  8) & 0xFF) << 16) | \
-		   ((((b) >> 16) & 0xFF) <<  8) | \
-		   ((((b) >> 24) & 0xFF)      ))
+#define htoll(b) \
+	(((((b)      ) & 0xFF) << 24) | \
+	((( (b) >>  8) & 0xFF) << 16) | \
+	((( (b) >> 16) & 0xFF) <<  8) | \
+	((( (b) >> 24) & 0xFF)      ))
 #define htols(b) \
-          (((((b)      ) & 0xFF) << 8) | \
-		   ((((b) >> 8) & 0xFF)      ))
+	(((((b)      ) & 0xFF) <<  8) | \
+	((( (b) >>  8) & 0xFF)      ))
 #define ltohl(b) htoll(b)
 #define ltohs(b) htols(b)
 #else
@@ -105,7 +105,9 @@ static int check_header(FILE *f)
 		ast_log(LOG_WARNING, "Read failed (size)\n");
 		return -1;
 	}
+#if __BYTE_ORDER == __BIG_ENDIAN
 	size = ltohl(size);
+#endif
 	if (fread(&formtype, 1, 4, f) != 4) {
 		ast_log(LOG_WARNING, "Read failed (formtype)\n");
 		return -1;
diff --git a/funcs/func_aes.c b/funcs/func_aes.c
index 47b35f0..3338118 100644
--- a/funcs/func_aes.c
+++ b/funcs/func_aes.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/pbx.h"
diff --git a/funcs/func_base64.c b/funcs/func_base64.c
index 583c1f8..1a7619d 100644
--- a/funcs/func_base64.c
+++ b/funcs/func_base64.c
@@ -28,7 +28,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 328259 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/pbx.h"	/* function register/unregister */
diff --git a/funcs/func_blacklist.c b/funcs/func_blacklist.c
index 9c2353a..bb04505 100644
--- a/funcs/func_blacklist.c
+++ b/funcs/func_blacklist.c
@@ -32,7 +32,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 411328 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/pbx.h"
 #include "asterisk/module.h"
diff --git a/funcs/func_callcompletion.c b/funcs/func_callcompletion.c
index e3f7b32..cb1cd59 100644
--- a/funcs/func_callcompletion.c
+++ b/funcs/func_callcompletion.c
@@ -27,7 +27,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 411328 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/channel.h"
diff --git a/funcs/func_callerid.c b/funcs/func_callerid.c
index 6f42e09..621a6f2 100644
--- a/funcs/func_callerid.c
+++ b/funcs/func_callerid.c
@@ -30,7 +30,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 411328 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/channel.h"
@@ -40,10 +40,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 411328 $")
 #include "asterisk/callerid.h"
 
 /*
- * Do not document the CALLERID(pres) datatype.
- * The name and number now have their own presentation value.  The pres
- * option will simply live on as a historical relic with as best
- * as can be managed backward compatible meaning.
+ * The CALLERID(pres) datatype is shorthand for getting/setting the
+ * combined value of name-pres and num-pres.  Some channel drivers
+ * don't make a distinction, so it makes sense to only use one property
+ * to get/set it.  The same applies to CONNECTEDLINE(pres),
+ * REDIRECTING(orig-pres), REDIRECTING(from-pres) and REDIRECTING(to-pres).
  *
  * Do not document the CALLERID(ton) datatype.
  * It is an alias for num-plan.
@@ -55,11 +56,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 411328 $")
  * It has turned out to not be needed.  The source value is really
  * only useful as a possible tracing aid.
  *
- * Do not document the CONNECTEDLINE(pres) datatype.
- * The name and number now have their own presentation value.  The pres
- * option will simply live on as a historical relic with as best
- * as can be managed backward compatible meaning.
- *
  * Do not document the CONNECTEDLINE(ton) datatype.
  * It is an alias for num-plan.
  *
@@ -69,12 +65,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 411328 $")
  * they are active at the same time.  The plain pres option will simply
  * live on as a historical relic.
  *
- * Do not document the REDIRECTING(orig-pres), REDIRECTING(from-pres),
- * or REDIRECTING(to-pres) datatypes.
- * The name and number now have their own presentation value.  The orig-pres,
- * from-pres, and to-pres options will simply live on as a historical relic
- * with as best as can be managed backward compatible meaning.
- *
  * Do not document the REDIRECTING(orig-ton), REDIRECTING(from-ton),
  * or REDIRECTING(to-ton) datatypes.
  * They are aliases for orig-num-plan, from-num-plan, and to-num-plan
@@ -98,6 +88,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 411328 $")
 					<enum name = "num-valid" />
 					<enum name = "num-plan" />
 					<enum name = "num-pres" />
+					<enum name = "pres" />
 					<enum name = "subaddr" />
 					<enum name = "subaddr-valid" />
 					<enum name = "subaddr-type" />
@@ -144,6 +135,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 411328 $")
 		<description>
 			<para>Gets or sets Caller*ID data on the channel. Uses channel callerid by
 			default or optional callerid, if specified.</para>
+			<para>The <replaceable>pres</replaceable> field gets/sets a combined value
+			for <replaceable>name-pres</replaceable> and
+			<replaceable>num-pres</replaceable>.</para>
 			<para>The allowable values for the <replaceable>name-charset</replaceable>
 			field are the following:</para>
 			<enumlist>
@@ -168,7 +162,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 411328 $")
 		<description>
 			<para>Gets or sets Caller*ID presentation on the channel.
 			This function is deprecated in favor of CALLERID(num-pres)
-			and CALLERID(name-pres).
+			and CALLERID(name-pres) or CALLERID(pres) to get/set both
+			at once.
 			The following values are valid:</para>
 			<enumlist>
 				<enum name="allowed_not_screened">
@@ -218,6 +213,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 411328 $")
 					<enum name = "num-valid" />
 					<enum name = "num-plan" />
 					<enum name = "num-pres" />
+					<enum name = "pres" />
 					<enum name = "subaddr" />
 					<enum name = "subaddr-valid" />
 					<enum name = "subaddr-type" />
@@ -246,6 +242,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 411328 $")
 		</syntax>
 		<description>
 			<para>Gets or sets Connected Line data on the channel.</para>
+			<para>The <replaceable>pres</replaceable> field gets/sets a combined value
+			for <replaceable>name-pres</replaceable> and
+			<replaceable>num-pres</replaceable>.</para>
 			<para>The allowable values for the <replaceable>name-charset</replaceable>
 			field are the following:</para>
 			<enumlist>
@@ -279,6 +278,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 411328 $")
 					<enum name = "orig-num-valid" />
 					<enum name = "orig-num-plan" />
 					<enum name = "orig-num-pres" />
+					<enum name = "orig-pres" />
 					<enum name = "orig-subaddr" />
 					<enum name = "orig-subaddr-valid" />
 					<enum name = "orig-subaddr-type" />
@@ -294,6 +294,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 411328 $")
 					<enum name = "from-num-valid" />
 					<enum name = "from-num-plan" />
 					<enum name = "from-num-pres" />
+					<enum name = "from-pres" />
 					<enum name = "from-subaddr" />
 					<enum name = "from-subaddr-valid" />
 					<enum name = "from-subaddr-type" />
@@ -308,6 +309,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 411328 $")
 					<enum name = "to-num-valid" />
 					<enum name = "to-num-plan" />
 					<enum name = "to-num-pres" />
+					<enum name = "to-pres" />
 					<enum name = "to-subaddr" />
 					<enum name = "to-subaddr-valid" />
 					<enum name = "to-subaddr-type" />
@@ -366,6 +368,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 411328 $")
 		</syntax>
 		<description>
 			<para>Gets or sets Redirecting data on the channel.</para>
+			<para>The <replaceable>orig-pres</replaceable>,
+			<replaceable>from-pres</replaceable> and <replaceable>to-pres</replaceable>
+			fields get/set a combined value for the corresponding
+			<replaceable>...-name-pres</replaceable> and <replaceable>...-num-pres</replaceable>
+			fields.</para>
 			<para>The allowable values for the <replaceable>reason</replaceable>
 			and <replaceable>orig-reason</replaceable> fields are the following:</para>
 			<enumlist>
diff --git a/funcs/func_cdr.c b/funcs/func_cdr.c
index 9042451..95d047e 100644
--- a/funcs/func_cdr.c
+++ b/funcs/func_cdr.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 426252 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/channel.h"
@@ -180,7 +180,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 426252 $")
 						<para>Write-Only</para>
 					</enum>
 					<enum name="disable">
-						<para>Disable CDRs for this channel.</para>
+						<para>Setting to 1 will disable CDRs for this channel.
+						Setting to 0 will enable CDRs for this channel.</para>
 						<para>Write-Only</para>
 					</enum>
 				</enumlist>
@@ -284,14 +285,16 @@ static void cdr_read_callback(void *data, struct stasis_subscription *sub, struc
 			struct timeval fmt_time;
 			struct ast_tm tm;
 			/* tv_usec is suseconds_t, which could be int or long */
+			long int tv_sec;
 			long int tv_usec;
 
-			if (sscanf(tempbuf, "%ld.%ld", &fmt_time.tv_sec, &tv_usec) != 2) {
+			if (sscanf(tempbuf, "%ld.%ld", &tv_sec, &tv_usec) != 2) {
 				ast_log(AST_LOG_WARNING, "Unable to parse %s (%s) from the CDR for channel %s\n",
 					args.variable, tempbuf, ast_channel_name(payload->chan));
 				return;
 			}
-			if (fmt_time.tv_sec) {
+			if (tv_sec) {
+				fmt_time.tv_sec = tv_sec;
 				fmt_time.tv_usec = tv_usec;
 				ast_localtime(&fmt_time, &tm, NULL);
 				ast_strftime(tempbuf, sizeof(tempbuf), "%Y-%m-%d %T", &tm);
diff --git a/funcs/func_channel.c b/funcs/func_channel.c
index ca45344..0f59bb5 100644
--- a/funcs/func_channel.c
+++ b/funcs/func_channel.c
@@ -30,7 +30,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 422700 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <regex.h>
 #include <ctype.h>
@@ -46,6 +46,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 422700 $")
 #include "asterisk/global_datastores.h"
 #include "asterisk/bridge_basic.h"
 #include "asterisk/bridge_after.h"
+#include "asterisk/max_forwards.h"
 
 /*** DOCUMENTATION
 	<function name="CHANNELS" language="en_US">
@@ -388,6 +389,16 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 422700 $")
 					<enum name="caller_url">
 						<para>R/0 Returns caller URL</para>
 					</enum>
+					<enum name="max_forwards">
+						<para>R/W Get or set the maximum number of call forwards for this channel.
+
+						This number describes the number of times a call may be forwarded by this channel
+						before the call fails. "Forwards" in this case refers to redirects by phones as well
+						as calls to local channels.
+
+						Note that this has no relation to the SIP Max-Forwards header.
+						</para>
+					</enum>
 				</enumlist>
 			</parameter>
 		</syntax>
@@ -432,7 +443,7 @@ static int func_channel_read(struct ast_channel *chan, const char *function,
 	if (!strcasecmp(data, "audionativeformat")) {
 		tmpcap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
 		if (tmpcap) {
-			struct ast_str *codec_buf = ast_str_alloca(128);
+			struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 			ast_channel_lock(chan);
 			ast_format_cap_append_from_cap(tmpcap, ast_channel_nativeformats(chan), AST_MEDIA_TYPE_AUDIO);
@@ -443,7 +454,7 @@ static int func_channel_read(struct ast_channel *chan, const char *function,
 	} else if (!strcasecmp(data, "videonativeformat")) {
 		tmpcap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
 		if (tmpcap) {
-			struct ast_str *codec_buf = ast_str_alloca(128);
+			struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 			ast_channel_lock(chan);
 			ast_format_cap_append_from_cap(tmpcap, ast_channel_nativeformats(chan), AST_MEDIA_TYPE_VIDEO);
@@ -508,22 +519,34 @@ static int func_channel_read(struct ast_channel *chan, const char *function,
 		}
 		ast_channel_unlock(chan);
 	} else if (!strcasecmp(data, "peer")) {
-		RAII_VAR(struct ast_channel *, p, NULL, ast_channel_cleanup);
-
-		ast_channel_lock(chan);
-		p = ast_channel_bridge_peer(chan);
-		if (p || ast_channel_tech(chan)) /* dummy channel? if so, we hid the peer name in the language */
-			ast_copy_string(buf, (p ? ast_channel_name(p) : ""), len);
-		else {
-			/* a dummy channel can still pass along bridged peer info via
-                           the BRIDGEPEER variable */
-			const char *pname = pbx_builtin_getvar_helper(chan, "BRIDGEPEER");
-			if (!ast_strlen_zero(pname))
-				ast_copy_string(buf, pname, len); /* a horrible kludge, but... how else? */
-			else
-				buf[0] = 0;
+		struct ast_channel *peer;
+
+		peer = ast_channel_bridge_peer(chan);
+		if (peer) {
+			/* Only real channels could have a bridge peer this way. */
+			ast_channel_lock(peer);
+			ast_copy_string(buf, ast_channel_name(peer), len);
+			ast_channel_unlock(peer);
+			ast_channel_unref(peer);
+		} else {
+			buf[0] = '\0';
+			ast_channel_lock(chan);
+			if (!ast_channel_tech(chan)) {
+				const char *pname;
+
+				/*
+				 * A dummy channel can still pass along bridged peer info
+				 * via the BRIDGEPEER variable.
+				 *
+				 * A horrible kludge, but... how else?
+				 */
+				pname = pbx_builtin_getvar_helper(chan, "BRIDGEPEER");
+				if (!ast_strlen_zero(pname)) {
+					ast_copy_string(buf, pname, len);
+				}
+			}
+			ast_channel_unlock(chan);
 		}
-		ast_channel_unlock(chan);
 	} else if (!strcasecmp(data, "uniqueid")) {
 		locked_copy_string(chan, buf, ast_channel_uniqueid(chan), len);
 	} else if (!strcasecmp(data, "transfercapability")) {
@@ -565,6 +588,10 @@ static int func_channel_read(struct ast_channel *chan, const char *function,
 			}
 		}
 		ast_channel_unlock(chan);
+	} else if (!strcasecmp(data, "max_forwards")) {
+		ast_channel_lock(chan);
+		snprintf(buf, len, "%d", ast_max_forwards_get(chan));
+		ast_channel_unlock(chan);
 	} else if (!ast_channel_tech(chan) || !ast_channel_tech(chan)->func_channel_read || ast_channel_tech(chan)->func_channel_read(chan, function, data, buf, len)) {
 		ast_log(LOG_WARNING, "Unknown or unavailable item requested: '%s'\n", data);
 		ret = -1;
@@ -725,6 +752,16 @@ static int func_channel_write_real(struct ast_channel *chan, const char *functio
 			store->media = ast_true(value) ? 1 : 0;
 		}
 		ast_channel_unlock(chan);
+	} else if (!strcasecmp(data, "max_forwards")) {
+		int max_forwards;
+		if (sscanf(value, "%d", &max_forwards) != 1) {
+			ast_log(LOG_WARNING, "Unable to set max forwards to '%s'\n", value);
+			ret = -1;
+		} else {
+			ast_channel_lock(chan);
+			ret = ast_max_forwards_set(chan, max_forwards);
+			ast_channel_unlock(chan);
+		}
 	} else if (!ast_channel_tech(chan)->func_channel_write
 		 || ast_channel_tech(chan)->func_channel_write(chan, function, data, value)) {
 		ast_log(LOG_WARNING, "Unknown or unavailable item requested: '%s'\n",
diff --git a/funcs/func_config.c b/funcs/func_config.c
index 9f935b7..ca6dad1 100644
--- a/funcs/func_config.c
+++ b/funcs/func_config.c
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 421337 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/channel.h"
@@ -49,6 +49,13 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 421337 $")
 			<parameter name="config_file" required="true" />
 			<parameter name="category" required="true" />
 			<parameter name="variable_name" required="true" />
+			<parameter name="index" required="false">
+				<para>If there are multiple variables with the same name, you can specify
+				<literal>0</literal> for the first item (default), <literal>-1</literal> for the last
+				item, or any other number for that specific item.  <literal>-1</literal> is useful
+				when the variable is derived from a template and you want the effective value (the last
+				occurrence), not the value from the template (the first occurrence).</para>
+			</parameter>
 		</syntax>
 		<description>
 			<para>This function reads a variable from an Asterisk configuration file.</para>
@@ -65,14 +72,17 @@ struct config_item {
 
 static AST_RWLIST_HEAD_STATIC(configs, config_item);
 
-static int config_function_read(struct ast_channel *chan, const char *cmd, char *data, 
-	char *buf, size_t len) 
+static int config_function_read(struct ast_channel *chan, const char *cmd, char *data,
+	char *buf, size_t len)
 {
 	struct ast_config *cfg;
 	struct ast_flags cfg_flags = { CONFIG_FLAG_FILEUNCHANGED };
-	const char *val;
 	char *parse;
 	struct config_item *cur;
+	int index = 0;
+	struct ast_variable *var;
+	struct ast_variable *found = NULL;
+	int ix = 0;
 	AST_DECLARE_APP_ARGS(args,
 		AST_APP_ARG(filename);
 		AST_APP_ARG(category);
@@ -103,6 +113,13 @@ static int config_function_read(struct ast_channel *chan, const char *cmd, char
 		return -1;
 	}
 
+	if (!ast_strlen_zero(args.index)) {
+		if (!sscanf(args.index, "%d", &index)) {
+			ast_log(LOG_ERROR, "AST_CONFIG() index must be an integer\n");
+			return -1;
+		}
+	}
+
 	if (!(cfg = ast_config_load(args.filename, cfg_flags)) || cfg == CONFIG_STATUS_FILEINVALID) {
 		return -1;
 	}
@@ -164,14 +181,29 @@ static int config_function_read(struct ast_channel *chan, const char *cmd, char
 		}
 	}
 
-	if (!(val = ast_variable_retrieve(cfg, args.category, args.variable))) {
-		ast_debug(1, "'%s' not found in [%s] of '%s'\n", args.variable,
-			args.category, args.filename);
+	for (var = ast_category_root(cfg, args.category); var; var = var->next) {
+		if (strcasecmp(args.variable, var->name)) {
+			continue;
+		}
+		found = var;
+		if (index == -1) {
+			continue;
+		}
+		if (ix == index) {
+			break;
+		}
+		found = NULL;
+		ix++;
+	}
+
+	if (!found) {
+		ast_debug(1, "'%s' not found at index %d in [%s] of '%s'.  Maximum index found: %d\n",
+			args.variable, index, args.category, args.filename, ix);
 		AST_RWLIST_UNLOCK(&configs);
 		return -1;
 	}
 
-	ast_copy_string(buf, val, len);
+	ast_copy_string(buf, found->value, len);
 
 	/* Unlock down here, so there's no chance the struct goes away while we're using it. */
 	AST_RWLIST_UNLOCK(&configs);
diff --git a/funcs/func_curl.c b/funcs/func_curl.c
index daf876e..61b0590 100644
--- a/funcs/func_curl.c
+++ b/funcs/func_curl.c
@@ -37,7 +37,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 431327 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <curl/curl.h>
 
@@ -171,7 +171,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 431327 $")
 #define CURLVERSION_ATLEAST(a,b,c) \
 	((LIBCURL_VERSION_MAJOR > (a)) || ((LIBCURL_VERSION_MAJOR == (a)) && (LIBCURL_VERSION_MINOR > (b))) || ((LIBCURL_VERSION_MAJOR == (a)) && (LIBCURL_VERSION_MINOR == (b)) && (LIBCURL_VERSION_PATCH >= (c))))
 
-#define CURLOPT_SPECIAL_HASHCOMPAT -500
+#define CURLOPT_SPECIAL_HASHCOMPAT ((CURLoption) -500)
 
 static void curlds_free(void *data);
 
@@ -199,6 +199,7 @@ static void curlds_free(void *data)
 		free(setting);
 	}
 	AST_LIST_HEAD_DESTROY(list);
+	ast_free(list);
 }
 
 enum optiontype {
@@ -653,6 +654,7 @@ static int acf_curl_helper(struct ast_channel *chan, const char *cmd, char *info
 			curl_easy_setopt(*curl, cur->key, cur->value);
 		}
 	}
+	AST_LIST_UNLOCK(&global_curl_info);
 
 	if (chan && (store = ast_channel_datastore_find(chan, &curl_info, NULL))) {
 		list = store->data;
@@ -691,7 +693,6 @@ static int acf_curl_helper(struct ast_channel *chan, const char *cmd, char *info
 	if (store) {
 		AST_LIST_UNLOCK(list);
 	}
-	AST_LIST_UNLOCK(&global_curl_info);
 
 	if (args.postdata) {
 		curl_easy_setopt(*curl, CURLOPT_POST, 0);
diff --git a/funcs/func_cut.c b/funcs/func_cut.c
index 0a4fe20..4b6dc9d 100644
--- a/funcs/func_cut.c
+++ b/funcs/func_cut.c
@@ -30,7 +30,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 370655 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/file.h"
 #include "asterisk/channel.h"
diff --git a/funcs/func_db.c b/funcs/func_db.c
index 315eaa7..b56fef9 100644
--- a/funcs/func_db.c
+++ b/funcs/func_db.c
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428413 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <regex.h>
 
diff --git a/funcs/func_devstate.c b/funcs/func_devstate.c
index 7a66988..88a5c5c 100644
--- a/funcs/func_devstate.c
+++ b/funcs/func_devstate.c
@@ -36,7 +36,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/channel.h"
diff --git a/funcs/func_dialgroup.c b/funcs/func_dialgroup.c
index 7fcaf89..37aba6a 100644
--- a/funcs/func_dialgroup.c
+++ b/funcs/func_dialgroup.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 398760 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/stat.h>
 
diff --git a/funcs/func_dialplan.c b/funcs/func_dialplan.c
index cd60c91..0f3c0f8 100644
--- a/funcs/func_dialplan.c
+++ b/funcs/func_dialplan.c
@@ -30,7 +30,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/channel.h"
diff --git a/funcs/func_enum.c b/funcs/func_enum.c
index 108578f..4b5fb9f 100644
--- a/funcs/func_enum.c
+++ b/funcs/func_enum.c
@@ -39,7 +39,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 331201 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/channel.h"
diff --git a/funcs/func_env.c b/funcs/func_env.c
index 33eff70..3c260a2 100644
--- a/funcs/func_env.c
+++ b/funcs/func_env.c
@@ -27,7 +27,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 413599 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/stat.h>   /* stat(2) */
 
@@ -561,7 +561,7 @@ static int file_read(struct ast_channel *chan, const char *cmd, char *data, stru
 
 			/* Don't go past the length requested */
 			if (off_i + toappend > offset + length) {
-				toappend = length - off_i;
+				toappend = MIN(offset + length - off_i, flength - off_i);
 			}
 
 			ast_str_append_substr(buf, len, fbuf, toappend);
@@ -725,7 +725,7 @@ static int file_read(struct ast_channel *chan, const char *cmd, char *data, stru
 				}
 			}
 			ast_debug(3, "length_offset=%" PRId64 ", length_offset - i=%" PRId64 "\n", length_offset, length_offset - i);
-			ast_str_append_substr(buf, len, fbuf, length_offset >= 0 ? length_offset - i : flength > i + sizeof(fbuf)) ? sizeof(fbuf) : flength - i;
+			ast_str_append_substr(buf, len, fbuf, (length_offset >= 0) ? length_offset - i : (flength > i + sizeof(fbuf)) ? sizeof(fbuf) : flength - i);
 
 			if (length_offset >= 0) {
 				break;
diff --git a/funcs/func_extstate.c b/funcs/func_extstate.c
index 53b0a56..940c455 100644
--- a/funcs/func_extstate.c
+++ b/funcs/func_extstate.c
@@ -32,7 +32,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 328259 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/channel.h"
diff --git a/funcs/func_frame_trace.c b/funcs/func_frame_trace.c
index bcf60bf..512d685 100644
--- a/funcs/func_frame_trace.c
+++ b/funcs/func_frame_trace.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 424472 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/channel.h"
diff --git a/funcs/func_global.c b/funcs/func_global.c
index af238e9..a2329c4 100644
--- a/funcs/func_global.c
+++ b/funcs/func_global.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 411328 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/stat.h>
 
diff --git a/funcs/func_groupcount.c b/funcs/func_groupcount.c
index 610a6a7..fd775d9 100644
--- a/funcs/func_groupcount.c
+++ b/funcs/func_groupcount.c
@@ -27,7 +27,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 411328 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/channel.h"
@@ -50,7 +50,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 411328 $")
 		</syntax>
 		<description>
 			<para>Calculates the group count for the specified group, or uses the
-			channel's current group if not specifed (and non-empty).</para>
+			channel's current group if not specified (and non-empty).</para>
 		</description>
 	</function>
 	<function name="GROUP_MATCH_COUNT" language="en_US">
diff --git a/funcs/func_hangupcause.c b/funcs/func_hangupcause.c
index db77438..983a0e1 100644
--- a/funcs/func_hangupcause.c
+++ b/funcs/func_hangupcause.c
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 413589 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/channel.h"
@@ -48,7 +48,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 413589 $")
 		</synopsis>
 		<syntax>
 			<parameter name="channel" required="true">
-				<para>The name of the channel for which to retreive cause information.</para>
+				<para>The name of the channel for which to retrieve cause information.</para>
 			</parameter>
 			<parameter name="type" required="true">
 				<para>Parameter describing which type of information is requested. Types are:</para>
diff --git a/funcs/func_holdintercept.c b/funcs/func_holdintercept.c
new file mode 100644
index 0000000..56d9a9e
--- /dev/null
+++ b/funcs/func_holdintercept.c
@@ -0,0 +1,236 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2015, Digium, Inc.
+ *
+ * Matt Jordan <mjordan at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Function that intercepts HOLD frames from channels and raises events
+ *
+ * \author Matt Jordan <mjordan at digium.com>
+ *
+ * \ingroup functions
+ */
+
+/*** MODULEINFO
+	<support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/app.h"
+#include "asterisk/frame.h"
+#include "asterisk/stasis.h"
+#include "asterisk/stasis_channels.h"
+
+/*** DOCUMENTATION
+	<function name="HOLD_INTERCEPT" language="en_US">
+		<synopsis>
+			Intercepts hold frames on a channel and raises an event instead of passing the frame on
+		</synopsis>
+		<syntax>
+			<parameter name="action" required="true">
+				<optionlist>
+					<option name="remove">
+						<para>W/O. Removes the hold interception function.</para>
+					</option>
+					<option name="set">
+						<para>W/O. Enable hold interception on the channel. When
+						enabled, the channel will intercept any hold action that
+						is signalled from the device, and instead simply raise an
+						event (AMI/ARI) indicating that the channel wanted to put other
+						parties on hold.</para>
+					</option>
+				</optionlist>
+			</parameter>
+		</syntax>
+	</function>
+***/
+
+/*! \brief Private data structure used with the function's datastore */
+struct hold_intercept_data {
+	int framehook_id;
+};
+
+/*! \brief The channel datastore the function uses to store state */
+static const struct ast_datastore_info hold_intercept_datastore = {
+	.type = "hold_intercept",
+};
+
+/*! \internal \brief Disable hold interception on the channel */
+static int remove_hold_intercept(struct ast_channel *chan)
+{
+	struct ast_datastore *datastore = NULL;
+	struct hold_intercept_data *data;
+	SCOPED_CHANNELLOCK(chan_lock, chan);
+
+	datastore = ast_channel_datastore_find(chan, &hold_intercept_datastore, NULL);
+	if (!datastore) {
+		ast_log(AST_LOG_WARNING, "Cannot remove HOLD_INTERCEPT from %s: HOLD_INTERCEPT not currently enabled\n",
+		        ast_channel_name(chan));
+		return -1;
+	}
+	data = datastore->data;
+
+	if (ast_framehook_detach(chan, data->framehook_id)) {
+		ast_log(AST_LOG_WARNING, "Failed to remove HOLD_INTERCEPT framehook from channel %s\n",
+		        ast_channel_name(chan));
+		return -1;
+	}
+
+	if (ast_channel_datastore_remove(chan, datastore)) {
+		ast_log(AST_LOG_WARNING, "Failed to remove HOLD_INTERCEPT datastore from channel %s\n",
+		        ast_channel_name(chan));
+		return -1;
+	}
+	ast_datastore_free(datastore);
+
+	return 0;
+}
+
+/*! \brief Frame hook that is called to intercept hold/unhold */
+static struct ast_frame *hold_intercept_framehook(struct ast_channel *chan,
+	struct ast_frame *f, enum ast_framehook_event event, void *data)
+{
+	int frame_type;
+
+	if (!f || (event != AST_FRAMEHOOK_EVENT_WRITE)) {
+		return f;
+	}
+
+	if (f->frametype != AST_FRAME_CONTROL) {
+		return f;
+	}
+
+	frame_type = f->subclass.integer;
+	if (frame_type != AST_CONTROL_HOLD && frame_type != AST_CONTROL_UNHOLD) {
+		return f;
+	}
+
+	/* Munch munch */
+	ast_frfree(f);
+	f = &ast_null_frame;
+
+	ast_channel_publish_cached_blob(chan,
+		frame_type == AST_CONTROL_HOLD ? ast_channel_hold_type() : ast_channel_unhold_type(),
+		NULL);
+
+	return f;
+}
+
+/*! \brief Callback function which informs upstream if we are consuming a frame of a specific type */
+static int hold_intercept_framehook_consume(void *data, enum ast_frame_type type)
+{
+	return (type == AST_FRAME_CONTROL ? 1 : 0);
+}
+
+/*! \internal \brief Enable hold interception on the channel */
+static int set_hold_intercept(struct ast_channel *chan)
+{
+	struct ast_datastore *datastore;
+	struct hold_intercept_data *data;
+	static struct ast_framehook_interface hold_framehook_interface = {
+		.version = AST_FRAMEHOOK_INTERFACE_VERSION,
+		.event_cb = hold_intercept_framehook,
+		.consume_cb = hold_intercept_framehook_consume,
+		.disable_inheritance = 1,
+	};
+	SCOPED_CHANNELLOCK(chan_lock, chan);
+
+	datastore = ast_channel_datastore_find(chan, &hold_intercept_datastore, NULL);
+	if (datastore) {
+		ast_log(AST_LOG_WARNING, "HOLD_INTERCEPT already set on '%s'\n",
+		        ast_channel_name(chan));
+		return 0;
+	}
+
+	datastore = ast_datastore_alloc(&hold_intercept_datastore, NULL);
+	if (!datastore) {
+		return -1;
+	}
+
+	data = ast_calloc(1, sizeof(*data));
+	if (!data) {
+		ast_datastore_free(datastore);
+		return -1;
+	}
+
+	data->framehook_id = ast_framehook_attach(chan, &hold_framehook_interface);
+	if (data->framehook_id < 0) {
+		ast_log(AST_LOG_WARNING, "Failed to attach HOLD_INTERCEPT framehook to '%s'\n",
+		        ast_channel_name(chan));
+		ast_datastore_free(datastore);
+		ast_free(data);
+		return -1;
+	}
+	datastore->data = data;
+
+	ast_channel_datastore_add(chan, datastore);
+
+	return 0;
+}
+
+/*! \internal \brief HOLD_INTERCEPT write function callback */
+static int hold_intercept_fn_write(struct ast_channel *chan, const char *function,
+	char *data, const char *value)
+{
+	int res;
+
+	if (!chan) {
+		return -1;
+	}
+
+	if (ast_strlen_zero(data)) {
+		ast_log(AST_LOG_WARNING, "HOLD_INTERCEPT requires an argument\n");
+		return -1;
+	}
+
+	if (!strcasecmp(data, "set")) {
+		res = set_hold_intercept(chan);
+	} else if (!strcasecmp(data, "remove")) {
+		res = remove_hold_intercept(chan);
+	} else {
+		ast_log(AST_LOG_WARNING, "HOLD_INTERCEPT: unknown option %s\n", data);
+		res = -1;
+	}
+
+	return res;
+}
+
+/*! \brief Definition of the HOLD_INTERCEPT function */
+static struct ast_custom_function hold_intercept_function = {
+	.name = "HOLD_INTERCEPT",
+	.write = hold_intercept_fn_write,
+};
+
+/*! \internal \brief Unload the module */
+static int unload_module(void)
+{
+	return ast_custom_function_unregister(&hold_intercept_function);
+}
+
+/*! \internal \brief Load the module */
+static int load_module(void)
+{
+	return ast_custom_function_register(&hold_intercept_function) ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Hold interception dialplan function");
diff --git a/funcs/func_iconv.c b/funcs/func_iconv.c
index d90fe0f..c3d0286 100644
--- a/funcs/func_iconv.c
+++ b/funcs/func_iconv.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 413589 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <ctype.h>
 #include <iconv.h>
diff --git a/funcs/func_jitterbuffer.c b/funcs/func_jitterbuffer.c
index add25b7..e33b2c2 100644
--- a/funcs/func_jitterbuffer.c
+++ b/funcs/func_jitterbuffer.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420639 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/channel.h"
diff --git a/funcs/func_lock.c b/funcs/func_lock.c
index 2b11cfa..2102d5c 100644
--- a/funcs/func_lock.c
+++ b/funcs/func_lock.c
@@ -32,7 +32,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 403960 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <signal.h>
 
diff --git a/funcs/func_logic.c b/funcs/func_logic.c
index bb0f074..e371f7e 100644
--- a/funcs/func_logic.c
+++ b/funcs/func_logic.c
@@ -30,7 +30,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 370655 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/channel.h"
diff --git a/funcs/func_math.c b/funcs/func_math.c
index e70a44a..5c8d3bc 100644
--- a/funcs/func_math.c
+++ b/funcs/func_math.c
@@ -34,7 +34,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 411328 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <math.h>
 
@@ -482,13 +482,11 @@ AST_TEST_DEFINE(test_MATH_function)
 
 	ast_test_status_update(test, "Testing MATH() substitution ...\n");
 
-	if (!(expr = ast_str_create(16)) || !(result = ast_str_create(16))) {
-		if (expr) {
-			ast_free(expr);
-		}
-		if (result) {
-			ast_free(result);
-		}
+	if (!(expr = ast_str_create(16))) {
+		return AST_TEST_FAIL;
+	}
+	if (!(result = ast_str_create(16))) {
+		ast_free(expr);
 		return AST_TEST_FAIL;
 	}
 
diff --git a/funcs/func_md5.c b/funcs/func_md5.c
index 5cd10be..2e60185 100644
--- a/funcs/func_md5.c
+++ b/funcs/func_md5.c
@@ -32,7 +32,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 328259 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/pbx.h"
diff --git a/funcs/func_module.c b/funcs/func_module.c
index 6053942..b0b3461 100644
--- a/funcs/func_module.c
+++ b/funcs/func_module.c
@@ -28,7 +28,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 328259 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/pbx.h"
diff --git a/funcs/func_odbc.c b/funcs/func_odbc.c
index 9976801..088cd5a 100644
--- a/funcs/func_odbc.c
+++ b/funcs/func_odbc.c
@@ -34,7 +34,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/file.h"
diff --git a/funcs/func_periodic_hook.c b/funcs/func_periodic_hook.c
index c171594..cb019b4 100644
--- a/funcs/func_periodic_hook.c
+++ b/funcs/func_periodic_hook.c
@@ -35,7 +35,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/channel.h"
@@ -446,13 +446,9 @@ static struct ast_custom_function hook_function = {
 	.write = hook_write,
 };
 
-static struct ast_context *func_periodic_hook_context;
-
 static int unload_module(void)
 {
-	if (func_periodic_hook_context) {
-		ast_context_destroy(func_periodic_hook_context, AST_MODULE);
-	}
+	ast_context_destroy(NULL, AST_MODULE);
 
 	return ast_custom_function_unregister(&hook_function);
 }
@@ -461,9 +457,7 @@ static int load_module(void)
 {
 	int res;
 
-	func_periodic_hook_context = ast_context_find_or_create(NULL, NULL,
-			context_name, AST_MODULE);
-	if (!func_periodic_hook_context) {
+	if (!ast_context_find_or_create(NULL, NULL, context_name, AST_MODULE)) {
 		ast_log(LOG_ERROR, "Failed to create %s dialplan context.\n", context_name);
 		return AST_MODULE_LOAD_DECLINE;
 	}
diff --git a/funcs/func_pitchshift.c b/funcs/func_pitchshift.c
index b7b25e0..bec5f71 100644
--- a/funcs/func_pitchshift.c
+++ b/funcs/func_pitchshift.c
@@ -64,7 +64,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/channel.h"
diff --git a/funcs/func_pjsip_aor.c b/funcs/func_pjsip_aor.c
new file mode 100644
index 0000000..47b9ad6
--- /dev/null
+++ b/funcs/func_pjsip_aor.c
@@ -0,0 +1,186 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2015, Digium, Inc.
+ *
+ * Joshua Colp <jcolp 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 Get information about a PJSIP AOR
+ *
+ * \author \verbatim Joshua Colp <jcolp at digium.com> \endverbatim
+ *
+ * \ingroup functions
+ *
+ */
+
+/*** MODULEINFO
+	<support_level>core</support_level>
+	<depend>pjproject</depend>
+	<depend>res_pjsip</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <pjsip.h>
+#include <pjlib.h>
+
+#include "asterisk/app.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/sorcery.h"
+#include "asterisk/res_pjsip.h"
+
+/*** DOCUMENTATION
+	<function name="PJSIP_AOR" language="en_US">
+		<synopsis>
+			Get information about a PJSIP AOR
+		</synopsis>
+		<syntax>
+			<parameter name="name" required="true">
+				<para>The name of the AOR to query.</para>
+			</parameter>
+			<parameter name="field" required="true">
+				<para>The configuration option for the AOR to query for.
+				Supported options are those fields on the
+				<replaceable>aor</replaceable> object in
+				<filename>pjsip.conf</filename>.</para>
+				<enumlist>
+					<configOptionToEnum>
+						<xi:include xpointer="xpointer(/docs/configInfo[@name='res_pjsip']/configFile[@name='pjsip.conf']/configObject[@name='aor']/configOption)"/>
+					</configOptionToEnum>
+				</enumlist>
+			</parameter>
+		</syntax>
+	</function>
+***/
+
+static int pjsip_aor_function_read(struct ast_channel *chan,
+	const char *cmd, char *data, struct ast_str **buf, ssize_t len)
+{
+	struct ast_sorcery *pjsip_sorcery;
+	char *parsed_data = ast_strdupa(data);
+	RAII_VAR(struct ast_sip_aor *, aor_obj, NULL, ao2_cleanup);
+	int res = 0;
+
+	AST_DECLARE_APP_ARGS(args,
+		AST_APP_ARG(aor_name);
+		AST_APP_ARG(field_name);
+	);
+
+	/* Check for zero arguments */
+	if (ast_strlen_zero(parsed_data)) {
+		ast_log(AST_LOG_ERROR, "Cannot call %s without arguments\n", cmd);
+		return -1;
+	}
+
+	AST_STANDARD_APP_ARGS(args, parsed_data);
+
+	if (ast_strlen_zero(args.aor_name)) {
+		ast_log(AST_LOG_ERROR, "Cannot call %s without an AOR name to query\n", cmd);
+		return -1;
+	}
+
+	if (ast_strlen_zero(args.field_name)) {
+		ast_log(AST_LOG_ERROR, "Cannot call %s with an empty field name to query\n", cmd);
+		return -1;
+	}
+
+	pjsip_sorcery = ast_sip_get_sorcery();
+	if (!pjsip_sorcery) {
+		ast_log(AST_LOG_ERROR, "Unable to retrieve PJSIP configuration: sorcery object is NULL\n");
+		return -1;
+	}
+
+	aor_obj = ast_sorcery_retrieve_by_id(pjsip_sorcery, "aor", args.aor_name);
+	if (!aor_obj) {
+		ast_log(AST_LOG_WARNING, "Failed to retrieve information for AOR '%s'\n", args.aor_name);
+		return -1;
+	}
+
+	if (!strcmp(args.field_name, "contact")) {
+		/* The multiple fields handler for contact does not provide a list of contact object names, which is what we want, so we
+		 * handle contact specifically to provide this.
+		 */
+		RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
+		struct ao2_iterator i;
+		struct ast_sip_contact *contact;
+		int first = 1;
+
+		contacts = ast_sip_location_retrieve_aor_contacts(aor_obj);
+		if (!contacts) {
+			ast_log(LOG_WARNING, "Failed to retrieve contacts for AOR '%s'\n", args.aor_name);
+			return -1;
+		}
+
+		i = ao2_iterator_init(contacts, 0);
+		while ((contact = ao2_iterator_next(&i))) {
+			if (!first) {
+				ast_str_append(buf, len, "%s", ",");
+			}
+
+			ast_str_append(buf, len, "%s", ast_sorcery_object_get_id(contact));
+			first = 0;
+
+			ao2_ref(contact, -1);
+		}
+		ao2_iterator_destroy(&i);
+	} else {
+		struct ast_variable *change_set;
+		struct ast_variable *it_change_set;
+
+		change_set = ast_sorcery_objectset_create(pjsip_sorcery, aor_obj);
+		if (!change_set) {
+			ast_log(AST_LOG_WARNING, "Failed to retrieve information for AOR '%s': change set is NULL\n", args.aor_name);
+			return -1;
+		}
+
+		for (it_change_set = change_set; it_change_set; it_change_set = it_change_set->next) {
+			if (!strcmp(it_change_set->name, args.field_name)) {
+				ast_str_set(buf, len, "%s", it_change_set->value);
+				break;
+			}
+		}
+
+		if (!it_change_set) {
+			ast_log(AST_LOG_WARNING, "Unknown property '%s' for PJSIP AOR\n", args.field_name);
+			res = 1;
+		}
+
+		ast_variables_destroy(change_set);
+	}
+
+	return res;
+}
+
+
+static struct ast_custom_function pjsip_aor_function = {
+	.name = "PJSIP_AOR",
+	.read2 = pjsip_aor_function_read,
+};
+
+static int unload_module(void)
+{
+	return ast_custom_function_unregister(&pjsip_aor_function);
+}
+
+static int load_module(void)
+{
+	return ast_custom_function_register(&pjsip_aor_function);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Get information about a PJSIP AOR");
diff --git a/funcs/func_pjsip_contact.c b/funcs/func_pjsip_contact.c
new file mode 100644
index 0000000..005f29b
--- /dev/null
+++ b/funcs/func_pjsip_contact.c
@@ -0,0 +1,203 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2015, Digium, Inc.
+ *
+ * Joshua Colp <jcolp 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 Get information about a PJSIP contact
+ *
+ * \author \verbatim Joshua Colp <jcolp at digium.com> \endverbatim
+ *
+ * \ingroup functions
+ *
+ */
+
+/*** MODULEINFO
+	<support_level>core</support_level>
+	<depend>pjproject</depend>
+	<depend>res_pjsip</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <pjsip.h>
+#include <pjlib.h>
+
+#include "asterisk/app.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/sorcery.h"
+#include "asterisk/res_pjsip.h"
+
+/*** DOCUMENTATION
+	<function name="PJSIP_CONTACT" language="en_US">
+		<synopsis>
+			Get information about a PJSIP contact
+		</synopsis>
+		<syntax>
+			<parameter name="name" required="true">
+				<para>The name of the contact to query.</para>
+			</parameter>
+			<parameter name="field" required="true">
+				<para>The configuration option for the contact to query for.
+				Supported options are those fields on the
+				<replaceable>contact</replaceable> object.</para>
+				<enumlist>
+					<configOptionToEnum>
+						<xi:include xpointer="xpointer(/docs/configInfo[@name='res_pjsip']/configFile[@name='pjsip.conf']/configObject[@name='contact']/configOption)"/>
+					</configOptionToEnum>
+				</enumlist>
+			</parameter>
+		</syntax>
+	</function>
+***/
+
+static int contact_function_get_permanent(void *obj, void *arg, int flags)
+{
+	const char *id = arg;
+
+	if (!strcmp(ast_sorcery_object_get_id(obj), id)) {
+		return CMP_MATCH | CMP_STOP;
+	}
+
+	return 0;
+}
+
+static int pjsip_contact_function_read(struct ast_channel *chan,
+	const char *cmd, char *data, struct ast_str **buf, ssize_t len)
+{
+	struct ast_sorcery *pjsip_sorcery;
+	char *parsed_data = ast_strdupa(data);
+	char *contact_name;
+	RAII_VAR(struct ast_sip_contact *, contact_obj, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_sip_contact_status *, contact_status, NULL, ao2_cleanup);
+	int res = 0;
+
+	AST_DECLARE_APP_ARGS(args,
+		AST_APP_ARG(contact_name);
+		AST_APP_ARG(field_name);
+	);
+
+	/* Check for zero arguments */
+	if (ast_strlen_zero(parsed_data)) {
+		ast_log(AST_LOG_ERROR, "Cannot call %s without arguments\n", cmd);
+		return -1;
+	}
+
+	AST_STANDARD_APP_ARGS(args, parsed_data);
+
+	if (ast_strlen_zero(args.contact_name)) {
+		ast_log(AST_LOG_ERROR, "Cannot call %s without a contact name to query\n", cmd);
+		return -1;
+	}
+
+	if (ast_strlen_zero(args.field_name)) {
+		ast_log(AST_LOG_ERROR, "Cannot call %s with an empty field name to query\n", cmd);
+		return -1;
+	}
+
+	pjsip_sorcery = ast_sip_get_sorcery();
+	if (!pjsip_sorcery) {
+		ast_log(AST_LOG_ERROR, "Unable to retrieve PJSIP configuration: sorcery object is NULL\n");
+		return -1;
+	}
+
+	/* Determine if this is a permanent contact or a normal contact */
+	if ((contact_name = strstr(args.contact_name, "@@"))) {
+		size_t aor_name_len = contact_name - args.contact_name;
+		char aor_name[aor_name_len + 1];
+		RAII_VAR(struct ast_sip_aor *, aor_obj, NULL, ao2_cleanup);
+
+		/* Grab only the AOR name so we can retrieve the AOR which will give us the contact */
+		strncpy(aor_name, args.contact_name, aor_name_len);
+		aor_name[aor_name_len] = '\0';
+
+		aor_obj = ast_sorcery_retrieve_by_id(pjsip_sorcery, "aor", aor_name);
+		if (!aor_obj) {
+			ast_log(AST_LOG_WARNING, "Failed to retrieve information for contact '%s'\n", args.contact_name);
+			return -1;
+		}
+
+		contact_obj = ao2_callback(aor_obj->permanent_contacts, 0, contact_function_get_permanent, args.contact_name);
+	} else {
+		contact_obj = ast_sorcery_retrieve_by_id(pjsip_sorcery, "contact", args.contact_name);
+	}
+
+	if (!contact_obj) {
+		ast_log(AST_LOG_WARNING, "Failed to retrieve information for contact '%s'\n", args.contact_name);
+		return -1;
+	}
+
+	contact_status = ast_sorcery_retrieve_by_id(pjsip_sorcery, CONTACT_STATUS, ast_sorcery_object_get_id(contact_obj));
+
+	if (!strcmp(args.field_name, "status")) {
+		ast_str_set(buf, len, "%s", ast_sip_get_contact_status_label(contact_status->status));
+	} else if (!strcmp(args.field_name, "rtt")) {
+		if (contact_status->status == UNKNOWN) {
+			ast_str_set(buf, len, "%s", "N/A");
+		} else {
+			ast_str_set(buf, len, "%" PRId64, contact_status->rtt);
+		}
+	} else {
+		struct ast_variable *change_set;
+		struct ast_variable *it_change_set;
+
+		change_set = ast_sorcery_objectset_create(pjsip_sorcery, contact_obj);
+
+		if (!change_set) {
+			ast_log(AST_LOG_WARNING, "Failed to retrieve information for contact '%s': change set is NULL\n", args.contact_name);
+			return -1;
+		}
+
+		for (it_change_set = change_set; it_change_set; it_change_set = it_change_set->next) {
+			if (!strcmp(it_change_set->name, args.field_name)) {
+				ast_str_set(buf, len, "%s", it_change_set->value);
+				break;
+			}
+		}
+
+		if (!it_change_set) {
+			ast_log(AST_LOG_WARNING, "Unknown property '%s' for PJSIP contact\n", args.field_name);
+
+			res = 1;
+		}
+
+		ast_variables_destroy(change_set);
+	}
+
+	return res;
+}
+
+
+static struct ast_custom_function pjsip_contact_function = {
+	.name = "PJSIP_CONTACT",
+	.read2 = pjsip_contact_function_read,
+};
+
+static int unload_module(void)
+{
+	return ast_custom_function_unregister(&pjsip_contact_function);
+}
+
+static int load_module(void)
+{
+	return ast_custom_function_register(&pjsip_contact_function);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Get information about a PJSIP contact");
diff --git a/funcs/func_pjsip_endpoint.c b/funcs/func_pjsip_endpoint.c
index 98c884f..8d079ed 100644
--- a/funcs/func_pjsip_endpoint.c
+++ b/funcs/func_pjsip_endpoint.c
@@ -34,7 +34,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 403617 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <pjsip.h>
 #include <pjlib.h>
diff --git a/funcs/func_presencestate.c b/funcs/func_presencestate.c
index 2d76788..ad452df 100644
--- a/funcs/func_presencestate.c
+++ b/funcs/func_presencestate.c
@@ -28,7 +28,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/channel.h"
@@ -41,7 +41,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
 #include "asterisk/app.h"
 #ifdef TEST_FRAMEWORK
 #include "asterisk/test.h"
-#include <semaphore.h>
+#include "asterisk/sem.h"
 #endif
 
 /*** DOCUMENTATION
@@ -255,7 +255,9 @@ static enum ast_presence_state custom_presence_callback(const char *data, char *
 	char *_message;
 	char *_subtype;
 
-	ast_db_get(astdb_family, data, buf, sizeof(buf));
+	if (ast_db_get(astdb_family, data, buf, sizeof(buf))) {
+		return AST_PRESENCE_NOT_SET;
+	}
 
 	if (parse_data(buf, &state, &_subtype, &_message, &_options)) {
 		return AST_PRESENCE_INVALID;
@@ -664,7 +666,7 @@ AST_TEST_DEFINE(test_invalid_parse_data)
 struct test_cb_data {
 	struct ast_presence_state_message *presence_state;
 	/* That's right. I'm using a semaphore */
-	sem_t sem;
+	struct ast_sem sem;
 };
 
 static struct test_cb_data *test_cb_data_alloc(void)
@@ -675,7 +677,7 @@ static struct test_cb_data *test_cb_data_alloc(void)
 		return NULL;
 	}
 
-	if (sem_init(&cb_data->sem, 0, 0)) {
+	if (ast_sem_init(&cb_data->sem, 0, 0)) {
 		ast_free(cb_data);
 		return NULL;
 	}
@@ -686,7 +688,7 @@ static struct test_cb_data *test_cb_data_alloc(void)
 static void test_cb_data_destroy(struct test_cb_data *cb_data)
 {
 	ao2_cleanup(cb_data->presence_state);
-	sem_destroy(&cb_data->sem);
+	ast_sem_destroy(&cb_data->sem);
 	ast_free(cb_data);
 }
 
@@ -699,7 +701,7 @@ static void test_cb(void *userdata, struct stasis_subscription *sub, struct stas
 	cb_data->presence_state = stasis_message_data(msg);
 	ao2_ref(cb_data->presence_state, +1);
 
-	sem_post(&cb_data->sem);
+	ast_sem_post(&cb_data->sem);
 }
 
 static enum ast_test_result_state presence_change_common(struct ast_test *test,
@@ -727,7 +729,7 @@ static enum ast_test_result_state presence_change_common(struct ast_test *test,
 		return AST_TEST_FAIL;
 	}
 
-	sem_wait(&cb_data->sem);
+	ast_sem_wait(&cb_data->sem);
 
 	ast_copy_string(out_state, ast_presence_state2str(cb_data->presence_state->state), out_state_size);
 	ast_copy_string(out_subtype, cb_data->presence_state->subtype, out_subtype_size);
diff --git a/funcs/func_rand.c b/funcs/func_rand.c
index 01e0d05..6491b37 100644
--- a/funcs/func_rand.c
+++ b/funcs/func_rand.c
@@ -30,7 +30,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 389251 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/channel.h"
diff --git a/funcs/func_realtime.c b/funcs/func_realtime.c
index 6253957..a870ab4 100644
--- a/funcs/func_realtime.c
+++ b/funcs/func_realtime.c
@@ -30,7 +30,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 403960 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/file.h"
 #include "asterisk/channel.h"
diff --git a/funcs/func_sha1.c b/funcs/func_sha1.c
index da1a86e..c6b7575 100644
--- a/funcs/func_sha1.c
+++ b/funcs/func_sha1.c
@@ -30,7 +30,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 328259 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/pbx.h"
diff --git a/funcs/func_shell.c b/funcs/func_shell.c
index d3a1971..e403efc 100644
--- a/funcs/func_shell.c
+++ b/funcs/func_shell.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 403960 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/channel.h"
diff --git a/funcs/func_speex.c b/funcs/func_speex.c
index 738e6fc..d0d451b 100644
--- a/funcs/func_speex.c
+++ b/funcs/func_speex.c
@@ -39,7 +39,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419044 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <speex/speex_preprocess.h>
 #include "asterisk/module.h"
diff --git a/funcs/func_sprintf.c b/funcs/func_sprintf.c
index 7ffd2dc..4d5fc7a 100644
--- a/funcs/func_sprintf.c
+++ b/funcs/func_sprintf.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 328259 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <ctype.h>
 
diff --git a/funcs/func_srv.c b/funcs/func_srv.c
index d455029..3786a2f 100644
--- a/funcs/func_srv.c
+++ b/funcs/func_srv.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 413589 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/srv.h"
diff --git a/funcs/func_strings.c b/funcs/func_strings.c
index 7952a24..efa4bfd 100644
--- a/funcs/func_strings.c
+++ b/funcs/func_strings.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 416503 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <regex.h>
 #include <ctype.h>
diff --git a/funcs/func_sysinfo.c b/funcs/func_sysinfo.c
index 3072e93..8da94dd 100644
--- a/funcs/func_sysinfo.c
+++ b/funcs/func_sysinfo.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 413589 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #if defined(HAVE_SYSINFO)
 #include <sys/sysinfo.h>
diff --git a/funcs/func_talkdetect.c b/funcs/func_talkdetect.c
index 45d1f5e..ccc74fd 100644
--- a/funcs/func_talkdetect.c
+++ b/funcs/func_talkdetect.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 427204 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/channel.h"
diff --git a/funcs/func_timeout.c b/funcs/func_timeout.c
index a4c8de5..b757e99 100644
--- a/funcs/func_timeout.c
+++ b/funcs/func_timeout.c
@@ -30,7 +30,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 405436 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/channel.h"
diff --git a/funcs/func_uri.c b/funcs/func_uri.c
index bf1f550..f53b75f 100644
--- a/funcs/func_uri.c
+++ b/funcs/func_uri.c
@@ -34,7 +34,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 418654 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/channel.h"
diff --git a/funcs/func_version.c b/funcs/func_version.c
index 29e04d1..da733d1 100644
--- a/funcs/func_version.c
+++ b/funcs/func_version.c
@@ -28,7 +28,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 389251 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/channel.h"
diff --git a/funcs/func_vmcount.c b/funcs/func_vmcount.c
index 04099e1..6c98e72 100644
--- a/funcs/func_vmcount.c
+++ b/funcs/func_vmcount.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 404350 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <dirent.h>
 
diff --git a/funcs/func_volume.c b/funcs/func_volume.c
index 738bcf8..9fd0200 100644
--- a/funcs/func_volume.c
+++ b/funcs/func_volume.c
@@ -32,7 +32,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 411328 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/channel.h"
diff --git a/include/asterisk.h b/include/asterisk.h
index ee1a9c3..c501c44 100644
--- a/include/asterisk.h
+++ b/include/asterisk.h
@@ -86,6 +86,11 @@ int ast_pbx_init(void);				/*!< Provided by pbx.c */
  *
  * \retval 0 on success.
  * \retval -1 on error.
+ *
+ * \note This function should be rarely used in situations where
+ * something must be shutdown to avoid corruption, excessive data
+ * loss, or when external programs must be stopped.  All other
+ * cleanup in the core should use ast_register_cleanup.
  */
 int ast_register_atexit(void (*func)(void));
 
@@ -111,6 +116,42 @@ int ast_register_cleanup(void (*func)(void));
  */
 void ast_unregister_atexit(void (*func)(void));
 
+/*!
+ * \brief Cancel an existing shutdown and return to normal operation.
+ *
+ * \note Shutdown can be cancelled while the server is waiting for
+ * any existing channels to be destroyed before shutdown becomes
+ * irreversible.
+ *
+ * \return non-zero if shutdown cancelled.
+ */
+int ast_cancel_shutdown(void);
+
+/*!
+ * \details
+ * The server is preventing new channel creation in preparation for
+ * shutdown and may actively be releasing resources.  The shutdown
+ * process may be canceled by ast_cancel_shutdown() if it is not too
+ * late.
+ *
+ * \note The preparation to shutdown phase can be quite lengthy
+ * if we are gracefully shutting down.  How long existing calls will
+ * last is not up to us.
+ *
+ * \return non-zero if the server is preparing to or actively shutting down.
+ */
+int ast_shutting_down(void);
+
+/*!
+ * \return non-zero if the server is actively shutting down.
+ * \since 13.3.0
+ *
+ * \details
+ * The server is releasing resources and unloading modules.
+ * It won't be long now.
+ */
+int ast_shutdown_final(void);
+
 #if !defined(LOW_MEMORY)
 /*!
  * \brief Register the version of a source code file with the core.
@@ -118,6 +159,8 @@ void ast_unregister_atexit(void (*func)(void));
  * \param version the version string (typically a SVN revision keyword string)
  * \return nothing
  *
+ * \note As of 13.4.0, the \c version parameter is ignored.
+ *
  * This function should not be called directly, but instead the
  * ASTERISK_FILE_VERSION macro should be used to register a file with the core.
  */
@@ -134,12 +177,29 @@ void ast_register_file_version(const char *file, const char *version);
  */
 void ast_unregister_file_version(const char *file);
 
-/*! \brief Find version for given module name
+/*!
+ * \brief Find version for given module name
  * \param file Module name (i.e. chan_sip.so)
- * \return version string or NULL if the module is not found
+ *
+* \note As of 13.4.0, the file version is no longer tracked. As such,
+ * if the file exists, the Asterisk version will be returned.
+ *
+ * \retval NULL if the file doesn't exist.
+ * \retval The Asterisk version if the file does exist.
  */
 const char *ast_file_version_find(const char *file);
 
+/*!
+ * \brief Complete a source file name
+ * \param partial The partial name of the file to look up.
+ * \param n The n-th match to return.
+ *
+ * \retval NULL if there is no match for partial at the n-th position
+ * \retval Matching source file name
+ *
+ * \note A matching source file is allocataed on the heap, and must be
+ * free'd by the caller.
+ */
 char *ast_complete_source_filename(const char *partial, int n);
 
 /*!
diff --git a/include/asterisk/_private.h b/include/asterisk/_private.h
index 01a8352..d49de17 100644
--- a/include/asterisk/_private.h
+++ b/include/asterisk/_private.h
@@ -19,6 +19,7 @@ int load_modules(unsigned int);		/*!< Provided by loader.c */
 int load_pbx(void);			/*!< Provided by pbx.c */
 int init_logger(void);			/*!< Provided by logger.c */
 void close_logger(void);		/*!< Provided by logger.c */
+void logger_queue_start(void);		/*!< Provided by logger.c */
 void clean_time_zones(void);			/*!< Provided by localtime.c */
 int ast_term_init(void);		/*!< Provided by term.c */
 int astdb_init(void);			/*!< Provided by db.c */
@@ -51,20 +52,6 @@ void ast_msg_shutdown(void);        /*!< Provided by message.c */
 int aco_init(void);             /*!< Provided by config_options.c */
 
 /*!
- * \since 12
- * \brief Possible return types for \ref ast_module_reload
- */
-enum ast_module_reload_result {
-	AST_MODULE_RELOAD_SUCCESS = 0,      /*!< The module was reloaded succesfully */
-	AST_MODULE_RELOAD_QUEUED,           /*!< The module reload request was queued */
-	AST_MODULE_RELOAD_NOT_FOUND,        /*!< The requested module was not found */
-	AST_MODULE_RELOAD_ERROR,            /*!< An error occurred while reloading the module */
-	AST_MODULE_RELOAD_IN_PROGRESS,      /*!< A module reload request is already in progress */
-	AST_MODULE_RELOAD_UNINITIALIZED,    /*!< The module has not been initialized */
-	AST_MODULE_RELOAD_NOT_IMPLEMENTED,  /*!< This module doesn't support reloading */
-};
-
-/*!
  * \brief Initialize the bridging system.
  * \since 12.0.0
  *
@@ -83,20 +70,6 @@ int ast_bridging_init(void);
 int ast_local_init(void);
 
 /*!
- * \brief Reload asterisk modules.
- * \param name the name of the module to reload
- *
- * This function reloads the specified module, or if no modules are specified,
- * it will reload all loaded modules.
- *
- * \note Modules are reloaded using their reload() functions, not unloading
- * them and loading them again.
- *
- * \retval The \ref ast_module_reload_result status of the module load request
- */
-enum ast_module_reload_result ast_module_reload(const char *name);
-
-/*!
  * \brief Process reload requests received during startup.
  *
  * This function requests that the loader execute the pending reload requests
diff --git a/include/asterisk/app.h b/include/asterisk/app.h
index d5a0e27..6171dd4 100644
--- a/include/asterisk/app.h
+++ b/include/asterisk/app.h
@@ -595,6 +595,22 @@ int __ast_vm_register(const struct ast_vm_functions *vm_table, struct ast_module
  */
 void ast_vm_unregister(const char *module_name);
 
+#ifdef TEST_FRAMEWORK
+/*!
+ * \brief Swap out existing voicemail functions with a temporary set of functions for use with unit tests
+ *
+ * \param vm_table function table to use for testing
+ *
+ * \note ast_vm_test_swap_table_out should be called to restore the original set before testing concludes
+ */
+void ast_vm_test_swap_table_in(const struct ast_vm_functions *vm_table);
+
+/*!
+ * \brief Used after ast_vm_test_swap_table_in to restore the original set of voicemail functions
+ */
+void ast_vm_test_swap_table_out(void);
+#endif
+
 #define VM_GREETER_MODULE_VERSION 1
 
 /*! \brief Voicemail greeter function table definition. */
@@ -969,6 +985,8 @@ int ast_play_and_wait(struct ast_channel *chan, const char *fn);
  * \since 12
  */
 enum ast_record_if_exists {
+	/*! Return an Error State for IF_Exists */
+	AST_RECORD_IF_EXISTS_ERROR = -1,
 	/*! Fail the recording. */
 	AST_RECORD_IF_EXISTS_FAIL,
 	/*! Overwrite the existing recording. */
diff --git a/include/asterisk/ari.h b/include/asterisk/ari.h
index 00769ee..c3df46a 100644
--- a/include/asterisk/ari.h
+++ b/include/asterisk/ari.h
@@ -225,6 +225,11 @@ void ast_ari_response_ok(struct ast_ari_response *response,
 void ast_ari_response_no_content(struct ast_ari_response *response);
 
 /*!
+ * \brief Fill in a <tt>Accepted</tt> (202) \a ast_ari_response.
+ */
+void ast_ari_response_accepted(struct ast_ari_response *response);
+
+/*!
  * \brief Fill in a <tt>Created</tt> (201) \a ast_ari_response.
  * \param response Response to fill in.
  * \param url URL to the created resource.
diff --git a/include/asterisk/ast_version.h b/include/asterisk/ast_version.h
index 51ff481..1ceac30 100644
--- a/include/asterisk/ast_version.h
+++ b/include/asterisk/ast_version.h
@@ -41,4 +41,7 @@ const char *ast_get_version(void);
  */
 const char *ast_get_version_num(void);
 
+/*! Retreive the Asterisk build options */
+const char *ast_get_build_opts(void);
+
 #endif /* __AST_VERSION_H */
diff --git a/include/asterisk/audiohook.h b/include/asterisk/audiohook.h
index 375b2dd..cae8cc0 100644
--- a/include/asterisk/audiohook.h
+++ b/include/asterisk/audiohook.h
@@ -63,6 +63,7 @@ enum ast_audiohook_flags {
 	AST_AUDIOHOOK_SMALL_QUEUE   = (1 << 4),
 	AST_AUDIOHOOK_MUTE_READ     = (1 << 5), /*!< audiohook should be mute frames read */
 	AST_AUDIOHOOK_MUTE_WRITE    = (1 << 6), /*!< audiohook should be mute frames written */
+	AST_AUDIOHOOK_COMPATIBLE    = (1 << 7), /*!< is the audiohook native slin compatible */
 };
 
 enum ast_audiohook_init_flags {
diff --git a/include/asterisk/autoconfig.h.in b/include/asterisk/autoconfig.h.in
index 2752613..ec8295f 100644
--- a/include/asterisk/autoconfig.h.in
+++ b/include/asterisk/autoconfig.h.in
@@ -324,7 +324,7 @@
 /* Define to 1 if you have the Hoard Memory Allocator library. */
 #undef HAVE_HOARD
 
-/* Define to 1 if you have the `htonll' function. */
+/* Define to 1 if arpa/inet.h includes a htonll definition. */
 #undef HAVE_HTONLL
 
 /* Define to 1 if you have the iCal library. */
@@ -524,7 +524,7 @@
 /* Define to 1 if you have the newt library. */
 #undef HAVE_NEWT
 
-/* Define to 1 if you have the `ntohll' function. */
+/* Define to 1 if arpa/inet.h includes a ntohll definition. */
 #undef HAVE_NTOHLL
 
 /* Define to 1 if your C library can safely print NULL to string formats. */
@@ -548,6 +548,9 @@
 /* Define to 1 if CRYPTO has the OpenSSL Elliptic Curve Support feature. */
 #undef HAVE_OPENSSL_EC
 
+/* Define if your system has SSL_CTX_set_ecdh_auto declared. */
+#undef HAVE_OPENSSL_ECDH_AUTO
+
 /* Define to 1 if CRYPTO has the OpenSSL SRTP Extension Support feature. */
 #undef HAVE_OPENSSL_SRTP
 
@@ -578,9 +581,16 @@
 /* Define if your system has the PJPROJECT libraries. */
 #undef HAVE_PJPROJECT
 
+/* Define to 1 if PJPROJECT has the pjsip_get_dest_info support feature. */
+#undef HAVE_PJSIP_GET_DEST_INFO
+
 /* Define if your system has the PJSIP_REPLACE_MEDIA_STREAM headers. */
 #undef HAVE_PJSIP_REPLACE_MEDIA_STREAM
 
+/* Define to 1 if PJPROJECT has the pj_ssl_cert_load_from_files2 support
+   feature. */
+#undef HAVE_PJ_SSL_CERT_LOAD_FROM_FILES2
+
 /* Define to 1 if PJPROJECT has the PJSIP Transaction Group Lock Support
    feature. */
 #undef HAVE_PJ_TRANSACTION_GRP_LOCK
@@ -816,6 +826,12 @@
 /* Define to 1 if you have the ISDN SS7 library. */
 #undef HAVE_SS7
 
+/* Define if your system has the SSL_OP_NO_TLSV1_1 headers. */
+#undef HAVE_SSL_OP_NO_TLSV1_1
+
+/* Define if your system has the SSL_OP_NO_TLSV1_2 headers. */
+#undef HAVE_SSL_OP_NO_TLSV1_2
+
 /* Define to 1 if `stat' has the bug that it succeeds when given the
    zero-length file name argument. */
 #undef HAVE_STAT_EMPTY_STRING_BUG
@@ -1300,6 +1316,14 @@
 /* Number of bits in a file offset, on hosts where this is settable. */
 #undef _FILE_OFFSET_BITS
 
+/* Prevent clang array-bounds warning by not using strcmp from bits/string2.h
+   */
+#undef _HAVE_STRING_ARCH_strcmp
+
+/* Prevent clang array-bounds warning by not using strsep from bits/string2.h
+   */
+#undef _HAVE_STRING_ARCH_strsep
+
 /* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
 #undef _LARGEFILE_SOURCE
 
diff --git a/include/asterisk/bridge.h b/include/asterisk/bridge.h
index ccfb541..30ac095 100644
--- a/include/asterisk/bridge.h
+++ b/include/asterisk/bridge.h
@@ -241,6 +241,8 @@ struct ast_bridge_methods {
 	ast_bridge_notify_masquerade_fn notify_masquerade;
 	/*! Get the bridge merge priority. */
 	ast_bridge_merge_priority_fn get_merge_priority;
+	/*! Peek at swap channel before it can hang up, prior to push. */
+	ast_bridge_push_channel_fn push_peek;
 };
 
 /*! Softmix technology parameters. */
@@ -444,15 +446,19 @@ enum ast_bridge_join_flags {
 };
 
 /*!
- * \brief Join (blocking) a channel to a bridge
+ * \brief Join a channel to a bridge (blocking)
  *
  * \param bridge Bridge to join
  * \param chan Channel to join
- * \param swap Channel to swap out if swapping
+ * \param swap Channel to swap out if swapping (A channel reference is stolen.)
  * \param features Bridge features structure
  * \param tech_args Optional Bridging tech optimization parameters for this channel.
  * \param flags defined by enum ast_bridge_join_flags.
  *
+ * \note The passed in swap channel is always unreffed on return.  It is not a
+ * good idea to access the swap channel on return or for the caller to keep a
+ * reference to it.
+ *
  * \note Absolutely _NO_ locks should be held before calling
  * this function since it blocks.
  *
@@ -495,7 +501,7 @@ enum ast_bridge_impart_flags {
 };
 
 /*!
- * \brief Impart (non-blocking) a channel onto a bridge
+ * \brief Impart a channel to a bridge (non-blocking)
  *
  * \param bridge Bridge to impart on
  * \param chan Channel to impart (The channel reference is stolen if impart successful.)
@@ -503,6 +509,8 @@ enum ast_bridge_impart_flags {
  * \param features Bridge features structure.
  * \param flags defined by enum ast_bridge_impart_flags.
  *
+ * \note The given bridge must be unlocked when calling this function.
+ *
  * \note The features parameter must be NULL or obtained by
  * ast_bridge_features_new().  You must not dereference features
  * after calling even if the call fails.
@@ -738,6 +746,18 @@ int ast_bridge_suspend(struct ast_bridge *bridge, struct ast_channel *chan);
  */
 int ast_bridge_unsuspend(struct ast_bridge *bridge, struct ast_channel *chan);
 
+/*!
+ * \brief Sets BRIDGECHANNEL and BRIDGEPVTCALLID for a channel
+ *
+ * \pre chan must be locked before calling
+ *
+ * \param name channel name of the bridged peer
+ * \param pvtid Private CallID of the bridged peer
+ *
+ * \return nothing
+ */
+void ast_bridge_vars_set(struct ast_channel *chan, const char *name, const char *pvtid);
+
 struct ast_unreal_pvt;
 
 /*!
diff --git a/include/asterisk/bridge_channel.h b/include/asterisk/bridge_channel.h
index 1d071a0..03fe30e 100644
--- a/include/asterisk/bridge_channel.h
+++ b/include/asterisk/bridge_channel.h
@@ -656,11 +656,26 @@ int ast_bridge_channel_write_park(struct ast_bridge_channel *bridge_channel, con
 void ast_bridge_channel_kick(struct ast_bridge_channel *bridge_channel, int cause);
 
 /*!
+ * \brief Add a DTMF digit to the collected digits.
+ * \since 13.3.0
+ *
+ * \param bridge_channel Channel that received a DTMF digit.
+ * \param digit DTMF digit to add to collected digits
+ *
+ * \note Neither the bridge nor the bridge_channel locks should be held
+ * when entering this function.
+ *
+ * \note This is can only be called from within DTMF bridge hooks.
+ */
+void ast_bridge_channel_feature_digit_add(struct ast_bridge_channel *bridge_channel, int digit);
+
+/*!
  * \brief Add a DTMF digit to the collected digits to match against DTMF features.
  * \since 12.8.0
  *
  * \param bridge_channel Channel that received a DTMF digit.
  * \param digit DTMF digit to add to collected digits or 0 for timeout event.
+ * \param clear_digits clear the digits array prior to calling hooks
  *
  * \note Neither the bridge nor the bridge_channel locks should be held
  * when entering this function.
@@ -668,6 +683,10 @@ void ast_bridge_channel_kick(struct ast_bridge_channel *bridge_channel, int caus
  * \note This is intended to be called by bridge hooks and the
  * bridge channel thread.
  *
+ * \note This is intended to be called by non-DTMF bridge hooks and the bridge
+ * channel thread.  Calling from a DTMF bridge hook can potentially cause
+ * unbounded recursion.
+ *
  * \return Nothing
  */
 void ast_bridge_channel_feature_digit(struct ast_bridge_channel *bridge_channel, int digit);
diff --git a/include/asterisk/bridge_channel_internal.h b/include/asterisk/bridge_channel_internal.h
index b5a3a8d..e3fb73d 100644
--- a/include/asterisk/bridge_channel_internal.h
+++ b/include/asterisk/bridge_channel_internal.h
@@ -103,6 +103,9 @@ void bridge_channel_settle_owed_events(struct ast_bridge *orig_bridge, struct as
  *
  * \param bridge_channel Channel to push.
  *
+ * \note A ref is not held by bridge_channel->swap when calling because the
+ * push with swap happens immediately.
+ *
  * \note On entry, bridge_channel->bridge is already locked.
  *
  * \retval 0 on success.
@@ -127,19 +130,63 @@ int bridge_channel_internal_push(struct ast_bridge_channel *bridge_channel);
 void bridge_channel_internal_pull(struct ast_bridge_channel *bridge_channel);
 
 /*!
+ * \brief Internal bridge channel wait condition and associated result.
+ */
+struct bridge_channel_internal_cond {
+	/*! Lock for the data structure */
+	ast_mutex_t lock;
+	/*! Wait condition */
+	ast_cond_t cond;
+	/*! Wait until done */
+	int done;
+	/*! The bridge channel */
+	struct ast_bridge_channel *bridge_channel;
+};
+
+/*!
+ * \internal
+ * \brief Wait for the expected signal.
+ * \since 13.5.0
+ *
+ * \param cond the wait object
+ *
+ * \return Nothing
+ */
+void bridge_channel_internal_wait(struct bridge_channel_internal_cond *cond);
+
+/*!
  * \internal
- * \brief Join the bridge_channel to the bridge
+ * \brief Signal the condition wait.
+ * \since 13.5.0
+ *
+ * \param cond the wait object
+ *
+ * \return Nothing
+ */
+void bridge_channel_internal_signal(struct bridge_channel_internal_cond *cond);
+
+/*!
+ * \internal
+ * \brief Join the bridge_channel to the bridge (blocking)
  *
  * \param bridge_channel The Channel in the bridge
+ * \param cond data used for signaling
+ *
+ * \note The bridge_channel->swap holds a channel reference for the swap
+ * channel going into the bridging system.  The ref ensures that the swap
+ * pointer is valid for the bridge subclass push callbacks.  The pointer
+ * will be NULL on return if the ref was consumed.
+ *
+ * \details
+ * This API call puts the bridge_channel into the bridge and handles the
+ * bridge_channel's processing of events while it is in the bridge.  It
+ * will return when the channel has been instructed to leave the bridge.
  *
  * \retval 0 bridge channel successfully joined the bridge
  * \retval -1 bridge channel failed to join the bridge
- *
- * \note This API call starts the bridge_channel's processing of events while
- * it is in the bridge. It will return when the channel has been instructed to
- * leave the bridge.
  */
-int bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel);
+int bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel,
+				 struct bridge_channel_internal_cond *cond);
 
 /*!
  * \internal
diff --git a/include/asterisk/bridge_internal.h b/include/asterisk/bridge_internal.h
index e50e7f9..37c8418 100644
--- a/include/asterisk/bridge_internal.h
+++ b/include/asterisk/bridge_internal.h
@@ -117,6 +117,9 @@ struct ast_bridge *bridge_base_init(struct ast_bridge *self, uint32_t capabiliti
  * \param attempt_recovery TRUE if failure attempts to push channel back into original bridge.
  * \param optimized Indicates whether the move is part of an unreal channel optimization.
  *
+ * \note A ref is not held by bridge_channel->swap when calling because the
+ * move with swap happens immediately.
+ *
  * \note The dst_bridge and bridge_channel->bridge are assumed already locked.
  *
  * \retval 0 on success.
diff --git a/include/asterisk/bridge_technology.h b/include/asterisk/bridge_technology.h
index 9c39f74..7de573a 100644
--- a/include/asterisk/bridge_technology.h
+++ b/include/asterisk/bridge_technology.h
@@ -152,7 +152,10 @@ struct ast_bridge_technology {
 	 * \note On entry, bridge is already locked.
 	 */
 	int (*write)(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame);
-	/*! Formats that the bridge technology supports */
+	/*!
+	 * \brief Formats that the bridge technology supports
+	 * \note This is no longer used.  It remains for ABI compatibility.
+	 */
 	struct ast_format_cap *format_capabilities;
 	/*! TRUE if the bridge technology is currently suspended. */
 	unsigned int suspended:1;
diff --git a/include/asterisk/cel.h b/include/asterisk/cel.h
index 833b48b..350b4bf 100644
--- a/include/asterisk/cel.h
+++ b/include/asterisk/cel.h
@@ -39,6 +39,8 @@ extern "C" {
  * \brief CEL event types
  */
 enum ast_cel_event_type {
+	AST_CEL_INVALID_VALUE = -1,
+	AST_CEL_ALL = 0,
 	/*! \brief channel birth */
 	AST_CEL_CHANNEL_START = 1,
 	/*! \brief channel end */
@@ -75,7 +77,7 @@ enum ast_cel_event_type {
 	AST_CEL_LOCAL_OPTIMIZE = 17,
 };
 
-/*! 
+/*!
  * \brief Check to see if CEL is enabled
  *
  * \since 1.8
diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h
index 45e94ce..fe669ef 100644
--- a/include/asterisk/channel.h
+++ b/include/asterisk/channel.h
@@ -1510,6 +1510,14 @@ const struct ast_channel_tech *ast_get_channel_tech(const char *name);
 void ast_hangup(struct ast_channel *chan);
 
 /*!
+ * \brief Soft hangup all active channels.
+ * \since 13.3.0
+ *
+ * \return Nothing
+ */
+void ast_softhangup_all(void);
+
+/*!
  * \brief Softly hangup up a channel
  *
  * \param chan channel to be soft-hung-up
@@ -1944,6 +1952,21 @@ int ast_write_text(struct ast_channel *chan, struct ast_frame *frame);
 int ast_prod(struct ast_channel *chan);
 
 /*!
+ * \brief Set specific read path on channel.
+ * \since 13.4.0
+ *
+ * \param chan Channel to setup read path.
+ * \param raw_format Format to expect from the channel driver.
+ * \param core_format What the core wants to read.
+ *
+ * \pre chan is locked
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int ast_set_read_format_path(struct ast_channel *chan, struct ast_format *raw_format, struct ast_format *core_format);
+
+/*!
  * \brief Sets read format on channel chan from capabilities
  * Set read format for channel to whichever component of "format" is best.
  * \param chan channel to change
@@ -2202,23 +2225,12 @@ int ast_channel_defer_dtmf(struct ast_channel *chan);
 /*! Undo defer.  ast_read will return any DTMF characters that were queued */
 void ast_channel_undefer_dtmf(struct ast_channel *chan);
 
-/*! Initiate system shutdown -- prevents new channels from being allocated.
- * \param hangup  If "hangup" is non-zero, all existing channels will receive soft
- *  hangups */
-void ast_begin_shutdown(int hangup);
-
-/*! Cancels an existing shutdown and returns to normal operation */
-void ast_cancel_shutdown(void);
-
 /*! \return number of channels available for lookup */
 int ast_active_channels(void);
 
 /*! \return the number of channels not yet destroyed */
 int ast_undestroyed_channels(void);
 
-/*! \return non-zero if Asterisk is being shut down */
-int ast_shutting_down(void);
-
 /*! Activate a given generator */
 int ast_activate_generator(struct ast_channel *chan, struct ast_generator *gen, void *params);
 
@@ -3893,6 +3905,26 @@ enum ama_flags ast_channel_string2amaflag(const char *flag);
  */
 const char *ast_channel_amaflags2string(enum ama_flags flags);
 
+enum AST_MONITORING_STATE {
+	AST_MONITOR_RUNNING,
+	AST_MONITOR_PAUSED
+};
+
+/*! Responsible for channel monitoring data */
+struct ast_channel_monitor {
+	struct ast_filestream *read_stream;
+	struct ast_filestream *write_stream;
+	char read_filename[FILENAME_MAX];
+	char write_filename[FILENAME_MAX];
+	char filename_base[FILENAME_MAX];
+	char beep_id[64];
+	int filename_changed;
+	char *format;
+	int joinfiles;
+	enum AST_MONITORING_STATE state;
+	int (*stop)(struct ast_channel *chan, int need_lock);
+};
+
 /* ACCESSOR FUNTIONS */
 /*! \brief Set the channel name */
 void ast_channel_name_set(struct ast_channel *chan, const char *name);
@@ -3908,7 +3940,7 @@ void ast_channel_name_set(struct ast_channel *chan, const char *name);
  *
  * \li language
  * \li accountcode
- * \li peeracccount
+ * \li peeraccount
  * \li linkedid
  */
 DECLARE_STRINGFIELD_SETTERS_FOR(name);
@@ -4186,13 +4218,13 @@ void ast_channel_internal_bridged_channel_set(struct ast_channel *chan, struct a
 
 /*!
  * \since 11
- * \brief Retreive a comma-separated list of channels for which dialed cause information is available
+ * \brief Retrieve a comma-separated list of channels for which dialed cause information is available
  *
  * \details
  * This function makes use of datastore operations on the channel, so
  * it is important to lock the channel before calling this function.
  *
- * \param chan The channel from which to retreive information
+ * \param chan The channel from which to retrieve information
  * \retval NULL on allocation failure
  * \retval Pointer to an ast_str object containing the desired information which must be freed
  */
@@ -4200,7 +4232,7 @@ struct ast_str *ast_channel_dialed_causes_channels(const struct ast_channel *cha
 
 /*!
  * \since 11
- * \brief Retreive a ref-counted cause code information structure
+ * \brief Retrieve a ref-counted cause code information structure
  *
  * \details
  * This function makes use of datastore operations on the channel, so
@@ -4209,8 +4241,8 @@ struct ast_str *ast_channel_dialed_causes_channels(const struct ast_channel *cha
  * calling function must decrease the reference count when it is finished
  * with the object.
  *
- * \param chan The channel from which to retreive information
- * \param chan_name The name of the channel about which to retreive information
+ * \param chan The channel from which to retrieve information
+ * \param chan_name The name of the channel about which to retrieve information
  * \retval NULL on search failure
  * \retval Pointer to a ref-counted ast_control_pvt_cause_code object containing the desired information
  */
diff --git a/include/asterisk/config.h b/include/asterisk/config.h
index 7f0434d..8a375e5 100644
--- a/include/asterisk/config.h
+++ b/include/asterisk/config.h
@@ -47,6 +47,14 @@ enum {
 	CONFIG_FLAG_NOREALTIME    = (1 << 3),
 };
 
+/*! Flags for ast_config_text_file_save2()
+ */
+enum config_save_flags {
+	CONFIG_SAVE_FLAG_NONE = (0),
+	/*! Insure a context doesn't effectively change if a template changes (pre 13.2 behavior) */
+	CONFIG_SAVE_FLAG_PRESERVE_EFFECTIVE_CONTEXT = (1 << 0),
+};
+
 #define	CONFIG_STATUS_FILEMISSING	(void *)0
 #define	CONFIG_STATUS_FILEUNCHANGED	(void *)-1
 #define	CONFIG_STATUS_FILEINVALID	(void *)-2
@@ -86,7 +94,8 @@ struct ast_variable {
 
 	int lineno;
 	int object;		/*!< 0 for variable, 1 for object */
-	int blanklines; 	/*!< Number of blanklines following entry */
+	int blanklines;		/*!< Number of blanklines following entry */
+	int inherited;		/*!< 1 for inherited from template or other base */
 	struct ast_comment *precomments;
 	struct ast_comment *sameline;
 	struct ast_comment *trailing; /*!< the last object in the list will get assigned any trailing comments when EOF is hit */
@@ -326,6 +335,23 @@ const char *ast_variable_find(const struct ast_category *category, const char *v
 const char *ast_variable_find_in_list(const struct ast_variable *list, const char *variable);
 
 /*!
+ * \brief Gets the LAST occurrence of a variable from a variable list
+ *
+ * \param list The ast_variable list to search
+ * \param variable The name of the ast_variable you wish to fetch data for
+ *
+ * \details
+ * Iterates over a given ast_variable list to search for the last occurrence of an
+ * ast_variable entry with a name attribute matching the given name (variable).
+ * This is useful if the list has duplicate entries (such as in cases where entries
+ * are created by a template)
+ *
+ * \retval The variable value on success
+ * \retval NULL if unable to find it.
+ */
+const char *ast_variable_find_last_in_list(const struct ast_variable *list, const char *variable);
+
+/*!
  * \brief Retrieve a category if it exists
  *
  * \param config which config to use
@@ -835,8 +861,11 @@ void ast_category_append(struct ast_config *config, struct ast_category *cat);
  *
  * \details
  * This function is used to apply a base (template) to an existing category
+ *
+ * \retval 0 if succeeded
+ * \retval -1 if the memory allocation failed
  */
-void ast_category_inherit(struct ast_category *existing, const struct ast_category *base);
+int ast_category_inherit(struct ast_category *existing, const struct ast_category *base);
 
 /*!
  * \brief Removes and destroys all variables in a category
@@ -907,6 +936,28 @@ struct ast_variable *ast_variable_list_append_hint(struct ast_variable **head, s
 int ast_variable_update(struct ast_category *category, const char *variable,
 						const char *value, const char *match, unsigned int object);
 
+/*!
+ * \brief Save a config text file
+ * \since 13.2.0
+ *
+ * \param filename Filename
+ * \param cfg ast_config
+ * \param generator generator
+ * \param flags List of config_save_flags
+ *
+ * \return 0 on success or -1 on failure.
+ */
+int ast_config_text_file_save2(const char *filename, const struct ast_config *cfg, const char *generator, uint32_t flags);
+
+/*!
+ * \brief Save a config text file preserving the pre 13.2 behavior
+ *
+ * \param filename Filename
+ * \param cfg ast_config
+ * \param generator generator
+ *
+ * \return 0 on success or -1 on failure.
+ */
 int ast_config_text_file_save(const char *filename, const struct ast_config *cfg, const char *generator);
 int config_text_file_save(const char *filename, const struct ast_config *cfg, const char *generator) __attribute__((deprecated));
 
diff --git a/include/asterisk/dial.h b/include/asterisk/dial.h
index ab92198..c59257c 100644
--- a/include/asterisk/dial.h
+++ b/include/asterisk/dial.h
@@ -46,6 +46,7 @@ enum ast_dial_option {
 	AST_DIAL_OPTION_DISABLE_CALL_FORWARDING, /*!< Disable call forwarding on channels */
 	AST_DIAL_OPTION_PREDIAL,                 /*!< Execute a predial subroutine before dialing */
 	AST_DIAL_OPTION_DIAL_REPLACES_SELF,      /*!< The dial operation is a replacement for the requester */
+	AST_DIAL_OPTION_SELF_DESTROY,            /*!< Destroy self at end of ast_dial_run */
 	AST_DIAL_OPTION_MAX,                     /*!< End terminator -- must always remain last */
 };
 
diff --git a/include/asterisk/dsp.h b/include/asterisk/dsp.h
index 16262c0..7e84ebe 100644
--- a/include/asterisk/dsp.h
+++ b/include/asterisk/dsp.h
@@ -138,6 +138,9 @@ void ast_dsp_digitreset(struct ast_dsp *dsp);
 /*! \brief Select feature set */
 void ast_dsp_set_features(struct ast_dsp *dsp, int features);
 
+/*! \brief Get features */
+int ast_dsp_get_features(struct ast_dsp *dsp);
+
 /*! \brief Get pending DTMF/MF digits */
 int ast_dsp_getdigits(struct ast_dsp *dsp, char *buf, int max);
 
diff --git a/include/asterisk/endpoints.h b/include/asterisk/endpoints.h
index 663dd94..c9cb6b9 100644
--- a/include/asterisk/endpoints.h
+++ b/include/asterisk/endpoints.h
@@ -160,6 +160,16 @@ const char *ast_endpoint_get_resource(const struct ast_endpoint *endpoint);
 const char *ast_endpoint_get_id(const struct ast_endpoint *endpoint);
 
 /*!
+ * \brief Gets the state of the given endpoint.
+ *
+ * \param endpoint The endpoint.
+ * \return state.
+ * \return \c AST_ENDPOINT_UNKNOWN if endpoint is \c NULL.
+ * \since 13.4
+ */
+enum ast_endpoint_state ast_endpoint_get_state(const struct ast_endpoint *endpoint);
+
+/*!
  * \brief Updates the state of the given endpoint.
  *
  * \param endpoint Endpoint to modify.
diff --git a/include/asterisk/format.h b/include/asterisk/format.h
index 32f9f2b..2ce1b97 100644
--- a/include/asterisk/format.h
+++ b/include/asterisk/format.h
@@ -120,6 +120,18 @@ struct ast_format_interface {
 	 */
 	void (* const format_generate_sdp_fmtp)(const struct ast_format *format, unsigned int payload,
 		struct ast_str **str);
+
+	/*!
+	 * \since 13.6.0
+	 * \brief Retrieve a particular format attribute setting
+	 *
+	 * \param format The format containing attributes
+	 * \param name The name of the attribute to retrieve
+	 *
+	 * \retval NULL if the parameter is not set on the format
+	 * \retval non-NULL the format attribute value
+	 */
+	const void *(* const format_attribute_get)(const struct ast_format *format, const char *name);
 };
 
 /*!
@@ -204,6 +216,17 @@ struct ast_format *ast_format_attribute_set(const struct ast_format *format, con
 	const char *value);
 
 /*!
+ * \since 13.6.0
+ *
+ * \param format The format to retrieve the attribute from
+ * \param name Attribute name
+ *
+ * \retval non-NULL the attribute value
+ * \retval NULL the attribute does not exist or is unset
+ */
+const void *ast_format_attribute_get(const struct ast_format *format, const char *name);
+
+/*!
  * \brief This function is used to have a media format aware module parse and interpret
  * SDP attribute information. Once interpreted this information is stored on the format
  * itself using Asterisk format attributes.
@@ -276,6 +299,17 @@ void ast_format_set_attribute_data(struct ast_format *format, void *attribute_da
 const char *ast_format_get_name(const struct ast_format *format);
 
 /*!
+ * \brief Get the codec associated with a format
+ *
+ * \param format The media format
+ *
+ * \return The codec
+ *
+ * \note The reference count of the returned codec is increased by 1 and must be decremented
+ */
+struct ast_codec *ast_format_get_codec(const struct ast_format *format);
+
+/*!
  * \brief Get the codec identifier associated with a format
  *
  * \param format The media format
diff --git a/include/asterisk/format_cap.h b/include/asterisk/format_cap.h
index 94e81f8..a86fd21 100644
--- a/include/asterisk/format_cap.h
+++ b/include/asterisk/format_cap.h
@@ -321,6 +321,11 @@ int ast_format_cap_has_type(const struct ast_format_cap *cap, enum ast_media_typ
  */
 const char *ast_format_cap_get_names(struct ast_format_cap *cap, struct ast_str **buf);
 
+#ifndef AST_FORMAT_CAP_NAMES_LEN
+/*! Buffer size for callers of ast_format_cap_get_names to allocate. */
+#define AST_FORMAT_CAP_NAMES_LEN 384
+#endif
+
 /*!
  * \brief Determine if a format cap has no formats in it.
  *
diff --git a/include/asterisk/global_datastores.h b/include/asterisk/global_datastores.h
index 16267a8..2946ede 100644
--- a/include/asterisk/global_datastores.h
+++ b/include/asterisk/global_datastores.h
@@ -26,14 +26,8 @@
 
 #include "asterisk/channel.h"
 
-extern const struct ast_datastore_info dialed_interface_info;
 extern const struct ast_datastore_info secure_call_info;
 
-struct ast_dialed_interface {
-	AST_LIST_ENTRY(ast_dialed_interface) list;
-	char interface[1];
-};
-
 struct ast_secure_call_store {
 	unsigned int signaling:1;
 	unsigned int media:1;
diff --git a/include/asterisk/http.h b/include/asterisk/http.h
index ad91823..bb8973d 100644
--- a/include/asterisk/http.h
+++ b/include/asterisk/http.h
@@ -200,6 +200,28 @@ void ast_http_send(struct ast_tcptls_session_instance *ser, enum ast_http_method
 	int status_code, const char *status_title, struct ast_str *http_header,
 	struct ast_str *out, int fd, unsigned int static_content);
 
+/*!
+ * \brief Creates and sends a formatted http response message.
+ * \param ser                   TCP/TLS session object
+ * \param status_code           HTTP response code (200/401/403/404/500)
+ * \param status_title          English equivalent to the status_code parameter
+ * \param http_header_data      The formatted text to use in the http header
+ * \param text                  Additional informational text to use in the
+ *                              response
+ *
+ * \note Function constructs response headers from the status_code, status_title and
+ * http_header_data parameters.
+ *
+ * The response body is created as HTML content, from the status_code,
+ * status_title, and the text parameters.
+ *
+ * The http_header_data parameter will be freed as a result of calling function.
+ *
+ * \since 13.2.0
+ */
+void ast_http_create_response(struct ast_tcptls_session_instance *ser, int status_code,
+	const char *status_title, struct ast_str *http_header_data, const char *text);
+
 /*! \brief Send http "401 Unauthorized" response and close socket */
 void ast_http_auth(struct ast_tcptls_session_instance *ser, const char *realm, const unsigned long nonce, const unsigned long opaque, int stale, const char *text);
 
diff --git a/include/asterisk/http_websocket.h b/include/asterisk/http_websocket.h
index 3e07e60..5adc089 100644
--- a/include/asterisk/http_websocket.h
+++ b/include/asterisk/http_websocket.h
@@ -68,6 +68,24 @@ struct ast_websocket_server;
 struct ast_websocket;
 
 /*!
+ * \brief Callback from the HTTP request attempting to establish a websocket connection
+ *
+ * This callback occurs when an HTTP request is made to establish a websocket connection.
+ * Implementers of \ref ast_websocket_protocol can use this to deny a request, or to
+ * set up application specific data before invocation of \ref ast_websocket_callback.
+ *
+ * \param ser The TCP/TLS session
+ * \param parameters Parameters extracted from the request URI
+ * \param headers Headers included in the request
+ *
+ * \retval 0 The session should be accepted
+ * \retval -1 The session should be rejected. Note that the caller must send an error
+ * response using \ref ast_http_error.
+ * \since 13.5.0
+ */
+typedef int (*ast_websocket_pre_callback)(struct ast_tcptls_session_instance *ser, struct ast_variable *parameters, struct ast_variable *headers);
+
+/*!
  * \brief Callback for when a new connection for a sub-protocol is established
  *
  * \param session A WebSocket session structure
@@ -81,6 +99,32 @@ struct ast_websocket;
 typedef void (*ast_websocket_callback)(struct ast_websocket *session, struct ast_variable *parameters, struct ast_variable *headers);
 
 /*!
+ * \brief A websocket protocol implementation
+ *
+ * Users of the Websocket API can register themselves as a websocket
+ * protocol. See \ref ast_websocket_add_protocol2 and \ref ast_websocket_server_add_protocol2.
+ * Simpler implementations may use only \ref ast_websocket_add_protocol and
+ * \ref ast_websocket_server_add_protocol.
+ *
+ * \since 13.5.0
+ */
+struct ast_websocket_protocol {
+	/*! \brief Name of the protocol */
+	char *name;
+/*!
+ * \brief Protocol version. This prevents dynamically loadable modules from registering
+ * if this struct is changed.
+ */
+#define AST_WEBSOCKET_PROTOCOL_VERSION 1
+	/*! \brief Protocol version. Should be set to /ref AST_WEBSOCKET_PROTOCOL_VERSION */
+	unsigned int version;
+	/*! \brief Callback called when a new session is attempted. Optional. */
+	ast_websocket_pre_callback session_attempted;
+	/* \brief Callback called when a new session is established. Mandatory. */
+	ast_websocket_callback session_established;
+};
+
+/*!
  * \brief Creates a \ref websocket_server
  *
  * \retval New \ref websocket_server instance
@@ -98,6 +142,15 @@ AST_OPTIONAL_API(struct ast_websocket_server *, ast_websocket_server_create, (vo
 AST_OPTIONAL_API(int, ast_websocket_uri_cb, (struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_vars, struct ast_variable *headers), { return -1; });
 
 /*!
+ * \brief Allocate a websocket sub-protocol instance
+ *
+ * \retval An instance of \ref ast_websocket_protocol on success
+ * \retval NULL on error
+ * \since 13.5.0
+ */
+AST_OPTIONAL_API(struct ast_websocket_protocol *, ast_websocket_sub_protocol_alloc, (const char *name), {return NULL;});
+
+/*!
  * \brief Add a sub-protocol handler to the default /ws server
  *
  * \param name Name of the sub-protocol to register
@@ -109,10 +162,25 @@ AST_OPTIONAL_API(int, ast_websocket_uri_cb, (struct ast_tcptls_session_instance
 AST_OPTIONAL_API(int, ast_websocket_add_protocol, (const char *name, ast_websocket_callback callback), {return -1;});
 
 /*!
+ * \brief Add a sub-protocol handler to the default /ws server
+ *
+ * \param protocol The sub-protocol to register. Note that this must
+ * be allocated using /ref ast_websocket_sub_protocol_alloc.
+ *
+ * \note This method is reference stealing. It will steal the reference to \ref protocol
+ * on success.
+ *
+ * \retval 0 success
+ * \retval -1 if sub-protocol handler could not be registered
+ * \since 13.5.0
+ */
+AST_OPTIONAL_API(int, ast_websocket_add_protocol2, (struct ast_websocket_protocol *protocol), {return -1;});
+
+/*!
  * \brief Remove a sub-protocol handler from the default /ws server.
  *
  * \param name Name of the sub-protocol to unregister
- * \param callback Callback that was previously registered with the sub-protocol
+ * \param callback Session Established callback that was previously registered with the sub-protocol
  *
  * \retval 0 success
  * \retval -1 if sub-protocol was not found or if callback did not match
@@ -132,6 +200,22 @@ AST_OPTIONAL_API(int, ast_websocket_remove_protocol, (const char *name, ast_webs
 AST_OPTIONAL_API(int, ast_websocket_server_add_protocol, (struct ast_websocket_server *server, const char *name, ast_websocket_callback callback), {return -1;});
 
 /*!
+ * \brief Add a sub-protocol handler to the given server.
+ *
+ * \param server The server to add the sub-protocol to.
+ * \param protocol The sub-protocol to register. Note that this must
+ * be allocated using /ref ast_websocket_sub_protocol_alloc.
+ *
+ * \note This method is reference stealing. It will steal the reference to \ref protocol
+ * on success.
+ *
+ * \retval 0 success
+ * \retval -1 if sub-protocol handler could not be registered
+ * \since 13.5.0
+ */
+AST_OPTIONAL_API(int, ast_websocket_server_add_protocol2, (struct ast_websocket_server *server, struct ast_websocket_protocol *protocol), {return -1;});
+
+/*!
  * \brief Remove a sub-protocol handler from the given server.
  *
  * \param name Name of the sub-protocol to unregister
diff --git a/include/asterisk/inline_api.h b/include/asterisk/inline_api.h
index 5f6911d..291a838 100644
--- a/include/asterisk/inline_api.h
+++ b/include/asterisk/inline_api.h
@@ -25,12 +25,14 @@
   Small API functions that are candidates for inlining need to be specially
   declared and defined, to ensure that the 'right thing' always happens.
   For example:
-  	- there must _always_ be a non-inlined version of the function
+	- there must _always_ be a non-inlined version of the function
 	available for modules compiled out of the tree to link to
 	- references to a function that cannot be inlined (for any
 	reason that the compiler deems proper) must devolve into an
 	'extern' reference, instead of 'static', so that multiple
-	copies of the function body are not built in different modules
+	copies of the function body are not built in different modules.
+	However, since this doesn't work for clang, we go with 'static'
+	anyway and hope for the best!
 	- when LOW_MEMORY is defined, inlining should be disabled
 	completely, even if the compiler is configured to support it
 
@@ -46,8 +48,12 @@
 #if !defined(LOW_MEMORY) && !defined(DISABLE_INLINE)
 
 #if !defined(AST_API_MODULE)
+#if defined(__clang__) || defined(__GNUC_STDC_INLINE__)
+#define AST_INLINE_API(hdr, body) static hdr; static inline hdr body
+#else /* if defined(__clang__) */
 #define AST_INLINE_API(hdr, body) hdr; extern inline hdr body
-#else
+#endif
+#else /* if !defined(AST_API_MODULE) */
 #define AST_INLINE_API(hdr, body) hdr; hdr body
 #endif
 
diff --git a/include/asterisk/json.h b/include/asterisk/json.h
index 8cb74a4..28ebfbd 100644
--- a/include/asterisk/json.h
+++ b/include/asterisk/json.h
@@ -1010,13 +1010,24 @@ struct ast_party_id;
  */
 struct ast_json *ast_json_party_id(struct ast_party_id *party);
 
+enum ast_json_to_ast_vars_code {
+	/*! \brief Conversion successful */
+	AST_JSON_TO_AST_VARS_CODE_SUCCESS,
+	/*!
+	 * \brief Conversion failed because invalid value type supplied.
+	 * \note Only string values allowed.
+	 */
+	AST_JSON_TO_AST_VARS_CODE_INVALID_TYPE,
+	/*! \brief Conversion failed because of allocation failure. (Out Of Memory) */
+	AST_JSON_TO_AST_VARS_CODE_OOM,
+};
+
 /*!
  * \brief Convert a \c ast_json list of key/value pair tuples into a \c ast_variable list
  * \since 12.5.0
  *
  * \param json_variables The JSON blob containing the variable
  * \param variables An out reference to the variables to populate.
- *        The pointer to the variables should be NULL when calling this.
  *
  * \code
  * struct ast_json *json_variables = ast_json_pack("[ { s: s } ]", "foo", "bar");
@@ -1026,10 +1037,9 @@ struct ast_json *ast_json_party_id(struct ast_party_id *party);
  * res = ast_json_to_ast_variables(json_variables, &variables);
  * \endcode
  *
- * \retval 0 success
- * \retval -1 error
+ * \return Conversion enum ast_json_to_ast_vars_code status
  */
-int ast_json_to_ast_variables(struct ast_json *json_variables, struct ast_variable **variables);
+enum ast_json_to_ast_vars_code ast_json_to_ast_variables(struct ast_json *json_variables, struct ast_variable **variables);
 
 /*!@}*/
 
diff --git a/include/asterisk/lock.h b/include/asterisk/lock.h
index 7741654..35a244b 100644
--- a/include/asterisk/lock.h
+++ b/include/asterisk/lock.h
@@ -18,7 +18,7 @@
 
 /*! \file
  * \brief Asterisk locking-related definitions:
- * - ast_mutext_t, ast_rwlock_t and related functions;
+ * - ast_mutex_t, ast_rwlock_t and related functions;
  * - atomic arithmetic instructions;
  * - wrappers for channel locking.
  *
@@ -102,6 +102,12 @@
 
 struct ast_channel;
 
+/*!
+ * \brief Lock tracking information.
+ *
+ * \note Any changes to this struct MUST be reflected in the
+ * lock.c:restore_lock_tracking() function.
+ */
 struct ast_lock_track {
 	const char *file[AST_MAX_REENTRANCY];
 	int lineno[AST_MAX_REENTRANCY];
@@ -474,45 +480,6 @@ static inline void ast_reentrancy_unlock(struct ast_lock_track *lt)
 	}
 }
 
-static inline void ast_reentrancy_init(struct ast_lock_track **plt)
-{
-	int i;
-	pthread_mutexattr_t reentr_attr;
-	struct ast_lock_track *lt = *plt;
-
-	if (!lt) {
-		lt = *plt = (struct ast_lock_track *) calloc(1, sizeof(*lt));
-	}
-
-	for (i = 0; i < AST_MAX_REENTRANCY; i++) {
-		lt->file[i] = NULL;
-		lt->lineno[i] = 0;
-		lt->func[i] = NULL;
-		lt->thread[i] = 0;
-#ifdef HAVE_BKTR
-		memset(&lt->backtrace[i], 0, sizeof(lt->backtrace[i]));
-#endif
-	}
-
-	lt->reentrancy = 0;
-
-	pthread_mutexattr_init(&reentr_attr);
-	pthread_mutexattr_settype(&reentr_attr, AST_MUTEX_KIND);
-	pthread_mutex_init(&lt->reentr_mutex, &reentr_attr);
-	pthread_mutexattr_destroy(&reentr_attr);
-}
-
-static inline void delete_reentrancy_cs(struct ast_lock_track **plt)
-{
-	struct ast_lock_track *lt;
-	if (*plt) {
-		lt = *plt;
-		pthread_mutex_destroy(&lt->reentr_mutex);
-		free(lt);
-		*plt = NULL;
-	}
-}
-
 #else /* !DEBUG_THREADS */
 
 #define AO2_DEADLOCK_AVOIDANCE(obj) \
@@ -728,6 +695,7 @@ AST_INLINE_API(int ast_atomic_fetchadd_int(volatile int *p, int v),
 AST_INLINE_API(int ast_atomic_fetchadd_int(volatile int *p, int v),
 {
 	return OSAtomicAdd64(v, (int64_t *) p) - v;
+})
 #elif defined (__i386__) || defined(__x86_64__)
 #ifdef sun
 AST_INLINE_API(int ast_atomic_fetchadd_int(volatile int *p, int v),
@@ -774,6 +742,7 @@ AST_INLINE_API(int ast_atomic_dec_and_test(volatile int *p),
 AST_INLINE_API(int ast_atomic_dec_and_test(volatile int *p),
 {
 	return OSAtomicAdd64( -1, (int64_t *) p) == 0;
+})
 #else
 AST_INLINE_API(int ast_atomic_dec_and_test(volatile int *p),
 {
diff --git a/include/asterisk/logger.h b/include/asterisk/logger.h
index 84e6991..d06aecf 100644
--- a/include/asterisk/logger.h
+++ b/include/asterisk/logger.h
@@ -45,6 +45,13 @@ extern "C" {
 
 #define AST_CALLID_BUFFER_LENGTH 13
 
+enum ast_logger_results {
+	AST_LOGGER_SUCCESS = 0, /*!< Log channel was created or deleted successfully*/
+	AST_LOGGER_FAILURE = 1, /*!< Log channel already exists for create or doesn't exist for deletion of log channel */
+	AST_LOGGER_DECLINE = -1, /*!< Log channel request was not accepted */
+	AST_LOGGER_ALLOC_ERROR = -2 /*!< filename allocation error */
+};
+
 /*! \brief Used for sending a log message
 	This is the standard logger function.  Probably the only way you will invoke it would be something like this:
 	ast_log(AST_LOG_WHATEVER, "Problem with the %s Captain.  We should get some more.  Will %d be enough?\n", "flux capacitor", 10);
@@ -62,6 +69,17 @@ extern "C" {
 void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
 	__attribute__((format(printf, 5, 6)));
 
+/*!
+ * \brief Used for sending a log message with protection against recursion.
+ *
+ * \note This function should be used by all error messages that might be directly
+ * or indirectly caused by logging.
+ *
+ * \see ast_log for documentation on the parameters.
+ */
+void ast_log_safe(int level, const char *file, int line, const char *function, const char *fmt, ...)
+	__attribute__((format(printf, 5, 6)));
+
 /* XXX needs documentation */
 struct ast_callid;
 
@@ -81,6 +99,36 @@ void ast_log_callid(int level, const char *file, int line, const char *function,
 	__attribute__((format(printf, 6, 7)));
 
 /*!
+ * \brief Retrieve the existing log channels
+ * \param logentry A callback to an updater function
+ * \param data Data passed into the callback for manipulation
+ *
+ * For each of the logging channels, logentry will be executed with the
+ * channel file name, log type, status of the log, and configuration levels.
+ *
+ * \retval 0 on success
+ * \retval 1 on failure
+ * \retval -2 on allocation error
+ */
+int ast_logger_get_channels(int (*logentry)(const char *channel, const char *type,
+	const char *status, const char *configuration, void *data), void *data);
+
+/*!
+ * \brief Create a log channel
+ *
+ * \param log_channel Log channel to create
+ * \param components Logging config levels to add to the log channel
+ */
+int ast_logger_create_channel(const char *log_channel, const char *components);
+
+/*!
+ * \brief Delete the specified log channel
+ *
+ * \param log_channel The log channel to delete
+ */
+int ast_logger_remove_channel(const char *log_channel);
+
+/*!
  * \brief Log a backtrace of the current thread's execution stack to the Asterisk log
  */
 void ast_log_backtrace(void);
@@ -91,6 +139,13 @@ int logger_reload(void);
 /*! \brief Reload logger while rotating log files */
 int ast_logger_rotate(void);
 
+/*!
+ * \brief Rotate the specified log channel.
+ *
+ * \param log_channel The log channel to rotate
+ */
+int ast_logger_rotate_channel(const char *log_channel);
+
 void __attribute__((format(printf, 5, 6))) ast_queue_log(const char *queuename, const char *callid, const char *agent, const char *event, const char *fmt, ...);
 
 /*!
@@ -392,7 +447,7 @@ 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 && ast_debug_get_by_module(AST_MODULE) >= (level)))
+		|| (ast_opt_dbg_module && (int)ast_debug_get_by_module(AST_MODULE) >= (level)))
 
 /*!
  * \brief Log a DEBUG message
diff --git a/include/asterisk/manager.h b/include/asterisk/manager.h
index 387430f..b5ede54 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.6.0"
+#define AMI_VERSION                     "2.8.0"
 #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 */
 
@@ -299,9 +299,58 @@ void astman_send_response(struct mansession *s, const struct message *m, char *r
 /*! \brief Send ack in manager transaction */
 void astman_send_ack(struct mansession *s, const struct message *m, char *msg);
 
-/*! \brief Send ack in manager list transaction */
+/*!
+ * \brief Send ack in manager transaction to begin a list.
+ *
+ * \param s - AMI session control struct.
+ * \param m - AMI action request that started the list.
+ * \param msg - Message contents describing the list to follow.
+ * \param listflag - Should always be set to "start".
+ *
+ * \note You need to call astman_send_list_complete_start() and
+ * astman_send_list_complete_end() to send the AMI list completion event.
+ *
+ * \return Nothing
+ */
 void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag);
 
+/*!
+ * \brief Start the list complete event.
+ * \since 13.2.0
+ *
+ * \param s - AMI session control struct.
+ * \param m - AMI action request that started the list.
+ * \param event_name - AMI list complete event name.
+ * \param count - Number of items in the list.
+ *
+ * \note You need to call astman_send_list_complete_end() to end
+ * the AMI list completion event.
+ *
+ * \note Between calling astman_send_list_complete_start() and
+ * astman_send_list_complete_end() you can add additonal headers
+ * using astman_append().
+ *
+ * \return Nothing
+ */
+void astman_send_list_complete_start(struct mansession *s, const struct message *m, const char *event_name, int count);
+
+/*!
+ * \brief End the list complete event.
+ * \since 13.2.0
+ *
+ * \param s - AMI session control struct.
+ *
+ * \note You need to call astman_send_list_complete_start() to start
+ * the AMI list completion event.
+ *
+ * \note Between calling astman_send_list_complete_start() and
+ * astman_send_list_complete_end() you can add additonal headers
+ * using astman_append().
+ *
+ * \return Nothing
+ */
+void astman_send_list_complete_end(struct mansession *s);
+
 void __attribute__((format(printf, 2, 3))) astman_append(struct mansession *s, const char *fmt, ...);
 
 /*! \brief Determinie if a manager session ident is authenticated */
diff --git a/include/asterisk/max_forwards.h b/include/asterisk/max_forwards.h
new file mode 100644
index 0000000..3130b4b
--- /dev/null
+++ b/include/asterisk/max_forwards.h
@@ -0,0 +1,78 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2015, Digium, Inc.
+ *
+ * Mark Michelson <mmichelson 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.
+ */
+
+#ifndef MAX_FORWARDS_H
+
+struct ast_channel;
+
+/*!
+ * \brief Set the starting max forwards for a particular channel.
+ *
+ * \pre chan is locked
+ *
+ * \param starting_count The value to set the max forwards to.
+ * \param chan The channel on which to set the max forwards.
+ * \retval 0 Success
+ * \retval 1 Failure
+ */
+int ast_max_forwards_set(struct ast_channel *chan, int starting_count);
+
+/*!
+ * \brief Get the current max forwards for a particular channel.
+ *
+ * If the channel has not had max forwards set on it, then the channel
+ * will have the default max forwards set on it and that value will
+ * be returned.
+ *
+ * \pre chan is locked
+ *
+ * \param chan The channel to get the max forwards for.
+ * \return The current max forwards count on the channel
+ */
+int ast_max_forwards_get(struct ast_channel *chan);
+
+/*!
+ * \brief Decrement the max forwards count for a particular channel.
+ *
+ * If the channel has not had max forwards set on it, then the channel
+ * will have the default max forwards set on it and that value will
+ * not be decremented.
+ *
+ * \pre chan is locked
+ *
+ * \chan The channel for which the max forwards value should be decremented
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_max_forwards_decrement(struct ast_channel *chan);
+
+/*!
+ * \brief Reset the max forwards on a channel to its starting value.
+ *
+ * If the channel has not had max forwards set on it, then the channel
+ * will have the default max forwards set on it.
+ *
+ * \pre chan is locked.
+ *
+ * \param chan The channel on which to reset the max forwards count.
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_max_forwards_reset(struct ast_channel *chan);
+
+#endif /* MAX_FORWARDS_H */
diff --git a/include/asterisk/module.h b/include/asterisk/module.h
index 7f9cecf..d201aac 100644
--- a/include/asterisk/module.h
+++ b/include/asterisk/module.h
@@ -73,6 +73,20 @@ enum ast_module_load_result {
 	AST_MODULE_LOAD_FAILURE = -1,   /*!< Module could not be loaded properly */
 };
 
+/*!
+ * \since 12
+ * \brief Possible return types for \ref ast_module_reload
+ */
+enum ast_module_reload_result {
+	AST_MODULE_RELOAD_SUCCESS = 0,      /*!< The module was reloaded succesfully */
+	AST_MODULE_RELOAD_QUEUED,           /*!< The module reload request was queued */
+	AST_MODULE_RELOAD_NOT_FOUND,        /*!< The requested module was not found */
+	AST_MODULE_RELOAD_ERROR,            /*!< An error occurred while reloading the module */
+	AST_MODULE_RELOAD_IN_PROGRESS,      /*!< A module reload request is already in progress */
+	AST_MODULE_RELOAD_UNINITIALIZED,    /*!< The module has not been initialized */
+	AST_MODULE_RELOAD_NOT_IMPLEMENTED,  /*!< This module doesn't support reloading */
+};
+
 enum ast_module_support_level {
 	AST_MODULE_SUPPORT_UNKNOWN,
 	AST_MODULE_SUPPORT_CORE,
@@ -107,6 +121,20 @@ enum ast_module_load_result ast_load_resource(const char *resource_name);
  */
 int ast_unload_resource(const char *resource_name, enum ast_module_unload_mode);
 
+/*!
+ * \brief Reload asterisk modules.
+ * \param name the name of the module to reload
+ *
+ * This function reloads the specified module, or if no modules are specified,
+ * it will reload all loaded modules.
+ *
+ * \note Modules are reloaded using their reload() functions, not unloading
+ * them and loading them again.
+ *
+ * \retval The \ref ast_module_reload_result status of the module load request
+ */
+enum ast_module_reload_result ast_module_reload(const char *name);
+
 /*! 
  * \brief Notify when usecount has been changed.
  *
@@ -133,6 +161,43 @@ int ast_update_module_list(int (*modentry)(const char *module, const char *descr
                            const char *like);
 
 /*!
+ * \brief Ask for a list of modules, descriptions, use counts and status.
+ * \param modentry A callback to an updater function
+ * \param like
+ * \param data Data passed into the callback for manipulation
+ *
+ * For each of the modules loaded, modentry will be executed with the resource,
+ * description, and usecount values of each particular module.
+ *
+ * \return the number of modules loaded
+ * \since 13.5.0
+ */
+int ast_update_module_list_data(int (*modentry)(const char *module, const char *description,
+                                                int usecnt, const char *status, const char *like,
+                                                enum ast_module_support_level support_level,
+                                                void *data),
+                                const char *like, void *data);
+
+/*!
+ * \brief Ask for a list of modules, descriptions, use counts and status.
+ * \param modentry A callback to an updater function
+ * \param like
+ * \param data Data passed into the callback for manipulation
+ * \param condition The condition to meet
+ *
+ * For each of the modules loaded, modentry will be executed with the resource,
+ * description, and usecount values of each particular module.
+ *
+ * \return the number of conditions met
+ * \since 13.5.0
+ */
+int ast_update_module_list_condition(int (*modentry)(const char *module, const char *description,
+                                                     int usecnt, const char *status, const char *like,
+                                                     enum ast_module_support_level support_level,
+                                                     void *data, const char *condition),
+                                     const char *like, void *data, const char *condition);
+
+/*!
  * \brief Check if module with the name given is loaded
  * \param name Module name, like "chan_sip.so"
  * \retval 1 if true 
@@ -288,8 +353,31 @@ void __ast_module_user_hangup_all(struct ast_module *);
 #define ast_module_user_remove(user) __ast_module_user_remove(ast_module_info->self, user)
 #define ast_module_user_hangup_all() __ast_module_user_hangup_all(ast_module_info->self)
 
-struct ast_module *ast_module_ref(struct ast_module *);
-void ast_module_unref(struct ast_module *);
+struct ast_module *__ast_module_ref(struct ast_module *mod, const char *file, int line, const char *func);
+void __ast_module_shutdown_ref(struct ast_module *mod, const char *file, int line, const char *func);
+void __ast_module_unref(struct ast_module *mod, const char *file, int line, const char *func);
+
+/*!
+ * \brief Hold a reference to the module
+ * \param mod Module to reference
+ * \return mod
+ *
+ * \note A module reference will prevent the module
+ * from being unloaded.
+ */
+#define ast_module_ref(mod)           __ast_module_ref(mod, __FILE__, __LINE__, __PRETTY_FUNCTION__)
+/*!
+ * \brief Prevent unload of the module before shutdown
+ * \param mod Module to hold
+ *
+ * \note This should not be balanced by a call to ast_module_unref.
+ */
+#define ast_module_shutdown_ref(mod)  __ast_module_shutdown_ref(mod, __FILE__, __LINE__, __PRETTY_FUNCTION__)
+/*!
+ * \brief Release a reference to the module
+ * \param mod Module to release
+ */
+#define ast_module_unref(mod)         __ast_module_unref(mod, __FILE__, __LINE__, __PRETTY_FUNCTION__)
 
 #if defined(__cplusplus) || defined(c_plusplus)
 #define AST_MODULE_INFO(keystr, flags_to_set, desc, load_func, unload_func, reload_func, load_pri, support_level)	\
diff --git a/include/asterisk/monitor.h b/include/asterisk/monitor.h
index 6030221..377cb62 100644
--- a/include/asterisk/monitor.h
+++ b/include/asterisk/monitor.h
@@ -26,31 +26,11 @@
 #include "asterisk/channel.h"
 #include "asterisk/optional_api.h"
 
-enum AST_MONITORING_STATE {
-	AST_MONITOR_RUNNING,
-	AST_MONITOR_PAUSED
-};
-
 /* Streams recording control */
 #define X_REC_IN	1
 #define X_REC_OUT	2
 #define X_JOIN		4
 
-/*! Responsible for channel monitoring data */
-struct ast_channel_monitor {
-	struct ast_filestream *read_stream;
-	struct ast_filestream *write_stream;
-	char read_filename[FILENAME_MAX];
-	char write_filename[FILENAME_MAX];
-	char filename_base[FILENAME_MAX];
-	char beep_id[64];
-	int filename_changed;
-	char *format;
-	int joinfiles;
-	enum AST_MONITORING_STATE state;
-	int (*stop)(struct ast_channel *chan, int need_lock);
-};
-
 /* Start monitoring a channel */
 AST_OPTIONAL_API(int, ast_monitor_start,
 		 (struct ast_channel *chan, const char *format_spec,
diff --git a/include/asterisk/pbx.h b/include/asterisk/pbx.h
index 795af05..5997458 100644
--- a/include/asterisk/pbx.h
+++ b/include/asterisk/pbx.h
@@ -300,7 +300,21 @@ struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts,
 void ast_merge_contexts_and_delete(struct ast_context **extcontexts, struct ast_hashtab *exttable, const char *registrar);
 
 /*!
- * \brief Destroy a context (matches the specified context (or ANY context if NULL)
+ * \brief Destroy a context by name
+ *
+ * \param context Name of the context to destroy
+ * \param registrar who registered it
+ *
+ * You can optionally leave out the registrar parameter.  It will find it
+ * based on the context name.
+ *
+ * \retval -1 context not found
+ * \retval 0 Success
+ */
+int ast_context_destroy_by_name(const char *context, const char *registrar);
+
+/*!
+ * \brief Destroy a context (matches the specified context or ANY context if NULL)
  *
  * \param con context to destroy
  * \param registrar who registered it
diff --git a/include/asterisk/poll-compat.h b/include/asterisk/poll-compat.h
index a0f1fe9..cbb6109 100644
--- a/include/asterisk/poll-compat.h
+++ b/include/asterisk/poll-compat.h
@@ -9,7 +9,7 @@
  */
 
 /*---------------------------------------------------------------------------*\
-  $Id: poll-compat.h 284598 2010-09-02 05:02:54Z tilghman $
+  $Id$
 
   NAME
 
diff --git a/include/asterisk/res_fax.h b/include/asterisk/res_fax.h
index b0a1a22..2304da7 100644
--- a/include/asterisk/res_fax.h
+++ b/include/asterisk/res_fax.h
@@ -53,8 +53,8 @@ enum ast_fax_capabilities {
 enum ast_fax_modems {
 	/*! V.17 */
 	AST_FAX_MODEM_V17 = (1 << 0),
-	/*! V.27 */
-	AST_FAX_MODEM_V27 = (1 << 1),
+	/*! V.27ter */
+	AST_FAX_MODEM_V27TER = (1 << 1),
 	/*! V.29 */
 	AST_FAX_MODEM_V29 = (1 << 2),
 	/*! V.34 */
@@ -175,6 +175,8 @@ struct ast_fax_session_details {
 	struct ast_fax_t38_parameters our_t38_parameters;
 	/*! the other endpoint's T.38 session parameters, if any */
 	struct ast_fax_t38_parameters their_t38_parameters;
+	/*! T.38 negotiation in ms */
+	unsigned int t38timeout;
 	/*! the id of the t.38 gateway framehook for this channel */
 	int gateway_id;
 	/*! the timeout for this gateway in seconds */
diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h
index 2617e95..6ca56bd 100644
--- a/include/asterisk/res_pjsip.h
+++ b/include/asterisk/res_pjsip.h
@@ -91,6 +91,8 @@ struct ast_sip_transport {
 	AST_DECLARE_STRING_FIELDS(
 		/*! Certificate of authority list file */
 		AST_STRING_FIELD(ca_list_file);
+		/*! Certificate of authority list path */
+		AST_STRING_FIELD(ca_list_path);
 		/*! Public certificate file */
 		AST_STRING_FIELD(cert_file);
 		/*! Optional private key of the certificate file */
@@ -164,6 +166,12 @@ struct ast_sip_contact {
 	unsigned int qualify_frequency;
 	/*! If true authenticate the qualify if needed */
 	int authenticate_qualify;
+	/*! Qualify timeout. 0 is diabled. */
+	double qualify_timeout;
+	/*! Endpoint that added the contact, only available in observers */
+	struct ast_sip_endpoint *endpoint;
+	/*! The name of the aor this contact belongs to */
+	char *aor;
 };
 
 #define CONTACT_STATUS "contact_status"
@@ -173,7 +181,10 @@ struct ast_sip_contact {
  */
 enum ast_sip_contact_status_type {
 	UNAVAILABLE,
-	AVAILABLE
+	AVAILABLE,
+	UNKNOWN,
+	CREATED,
+	REMOVED,
 };
 
 /*!
@@ -190,6 +201,12 @@ struct ast_sip_contact_status {
 	struct timeval rtt_start;
 	/*! The round trip time in microseconds */
 	int64_t rtt;
+	/*! Last status for a contact (default - unavailable) */
+	enum ast_sip_contact_status_type last_status;
+	/*! The name of the aor this contact_status belongs to */
+	char *aor;
+	/*! The original contact's URI */
+	char *uri;
 };
 
 /*!
@@ -222,6 +239,8 @@ struct ast_sip_aor {
 	struct ao2_container *permanent_contacts;
 	/*! Determines whether SIP Path headers are supported */
 	unsigned int support_path;
+	/*! Qualify timeout. 0 is diabled. */
+	double qualify_timeout;
 };
 
 /*!
@@ -250,6 +269,8 @@ enum ast_sip_dtmf_mode {
 	AST_SIP_DTMF_INBAND,
 	/*! Use SIP INFO DTMF (blech) */
 	AST_SIP_DTMF_INFO,
+	/*! Use SIP 4733 if supported by the other side or INBAND if not */
+	AST_SIP_DTMF_AUTO,
 };
 
 /*!
@@ -271,21 +292,21 @@ enum ast_sip_auth_type {
 #define SIP_SORCERY_AUTH_TYPE "auth"
 
 struct ast_sip_auth {
-	/* Sorcery ID of the auth is its name */
+	/*! Sorcery ID of the auth is its name */
 	SORCERY_OBJECT(details);
 	AST_DECLARE_STRING_FIELDS(
-		/* Identification for these credentials */
+		/*! Identification for these credentials */
 		AST_STRING_FIELD(realm);
-		/* Authentication username */
+		/*! Authentication username */
 		AST_STRING_FIELD(auth_user);
-		/* Authentication password */
+		/*! Authentication password */
 		AST_STRING_FIELD(auth_pass);
-		/* Authentication credentials in MD5 format (hash of user:realm:pass) */
+		/*! Authentication credentials in MD5 format (hash of user:realm:pass) */
 		AST_STRING_FIELD(md5_creds);
 	);
-	/* The time period (in seconds) that a nonce may be reused */
+	/*! The time period (in seconds) that a nonce may be reused */
 	unsigned int nonce_lifetime;
-	/* Used to determine what to use when authenticating */
+	/*! Used to determine what to use when authenticating */
 	enum ast_sip_auth_type type;
 };
 
@@ -485,6 +506,12 @@ struct ast_sip_media_rtp_configuration {
 	enum ast_sip_session_media_encryption encryption;
 	/*! Do we want to optimistically support encryption if possible? */
 	unsigned int encryption_optimistic;
+	/*! Number of seconds between RTP keepalive packets */
+	unsigned int keepalive;
+	/*! Number of seconds before terminating channel due to lack of RTP (when not on hold) */
+	unsigned int timeout;
+	/*! Number of seconds before terminating channel due to lack of RTP (when on hold) */
+	unsigned int timeout_hold;
 };
 
 /*!
@@ -542,6 +569,8 @@ struct ast_sip_endpoint_media_configuration {
 	unsigned int tos_video;
 	/*! Priority for video streams */
 	unsigned int cos_video;
+	/*! Is g.726 packed in a non standard way */
+	unsigned int g726_non_standard;
 };
 
 /*!
@@ -609,6 +638,10 @@ struct ast_sip_endpoint {
 	enum ast_sip_session_redirect redirect_method;
 	/*! Variables set on channel creation */
 	struct ast_variable *channel_vars;
+	/*! Whether to place a 'user=phone' parameter into the request URI if user is a number */
+	unsigned int usereqphone;
+	/*! Do we send messages for connected line updates for unanswered incoming calls immediately to this endpoint? */
+	unsigned int rpid_immediate;
 };
 
 /*!
@@ -687,6 +720,18 @@ struct ast_sip_outbound_authenticator {
 	 */
 	int (*create_request_with_auth)(const struct ast_sip_auth_vector *auths, struct pjsip_rx_data *challenge,
 			struct pjsip_transaction *tsx, struct pjsip_tx_data **new_request);
+	/*!
+	 * \brief Create a new request with authentication credentials based on old request
+	 *
+	 * \param auths A vector of IDs of auth sorcery objects
+	 * \param challenge The SIP response with authentication challenge(s)
+	 * \param old_request The request that resulted in challenge(s)
+	 * \param new_request The new SIP request with challenge response(s)
+	 * \retval 0 Successfully created new request
+	 * \retval -1 Failed to create a new request
+	 */
+	int (*create_request_with_auth_from_old)(const struct ast_sip_auth_vector *auths, struct pjsip_rx_data *challenge,
+			struct pjsip_tx_data *old_request, struct pjsip_tx_data **new_request);
 };
 
 /*!
@@ -774,6 +819,23 @@ int ast_sip_register_outbound_authenticator(struct ast_sip_outbound_authenticato
 void ast_sip_unregister_outbound_authenticator(struct ast_sip_outbound_authenticator *auth);
 
 /*!
+ * \brief Register a SIP endpoint identifier with a name.
+ *
+ * An endpoint identifier's purpose is to determine which endpoint a given SIP
+ * message has come from.
+ *
+ * Multiple endpoint identifiers may be registered so that if an endpoint
+ * cannot be identified by one identifier, it may be identified by another.
+ *
+ * \param identifier The SIP endpoint identifier to register
+ * \param name The name of the endpoint identifier
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_sip_register_endpoint_identifier_with_name(struct ast_sip_endpoint_identifier *identifier,
+						   const char *name);
+
+/*!
  * \brief Register a SIP endpoint identifier
  *
  * An endpoint identifier's purpose is to determine which endpoint a given SIP
@@ -790,6 +852,10 @@ void ast_sip_unregister_outbound_authenticator(struct ast_sip_outbound_authentic
  * be sure to load individual endpoint identifier modules in the order you wish
  * for them to be run in modules.conf
  *
+ * \note endpoint identifiers registered using this method (no name specified)
+ *       are placed at the front of the endpoint identifiers list ahead of any
+ *       named identifiers.
+ *
  * \param identifier The SIP endpoint identifier to register
  * \retval 0 Success
  * \retval -1 Failure
@@ -836,46 +902,6 @@ pjsip_endpoint *ast_sip_get_pjsip_endpoint(void);
 struct ast_sorcery *ast_sip_get_sorcery(void);
 
 /*!
- * \brief Initialize transport support on a sorcery instance
- *
- * \retval -1 failure
- * \retval 0 success
- */
-int ast_sip_initialize_sorcery_transport(void);
-
-/*!
- * \brief Destroy transport support on a sorcery instance
- *
- * \retval -1 failure
- * \retval 0 success
- */
-int ast_sip_destroy_sorcery_transport(void);
-
-/*!
- * \brief Initialize qualify support on a sorcery instance
- *
- * \retval -1 failure
- * \retval 0 success
- */
-int ast_sip_initialize_sorcery_qualify(void);
-
-/*!
- * \brief Initialize location support on a sorcery instance
- *
- * \retval -1 failure
- * \retval 0 success
- */
-int ast_sip_initialize_sorcery_location(void);
-
-/*!
- * \brief Destroy location support on a sorcery instance
- *
- * \retval -1 failure
- * \retval 0 success
- */
-int ast_sip_destroy_sorcery_location(void);
-
-/*!
  * \brief Retrieve a named AOR
  *
  * \param aor_name Name of the AOR
@@ -914,6 +940,25 @@ struct ao2_container *ast_sip_location_retrieve_aor_contacts(const struct ast_si
 struct ast_sip_contact *ast_sip_location_retrieve_contact_from_aor_list(const char *aor_list);
 
 /*!
+ * \brief Retrieve all contacts from a list of AORs
+ *
+ * \param aor_list A comma-separated list of AOR names
+ * \retval NULL if no contacts available
+ * \retval non-NULL container (which must be freed) if contacts available
+ */
+struct ao2_container *ast_sip_location_retrieve_contacts_from_aor_list(const char *aor_list);
+
+/*!
+ * \brief Retrieve the first bound contact AND the AOR chosen from a list of AORs
+ *
+ * \param aor_list A comma-separated list of AOR names
+ * \param aor The chosen AOR
+ * \param contact The chosen contact
+ */
+ void ast_sip_location_retrieve_contact_and_aor_from_list(const char *aor_list, struct ast_sip_aor **aor,
+	struct ast_sip_contact **contact);
+
+/*!
  * \brief Retrieve a named contact
  *
  * \param contact_name Name of the contact
@@ -931,12 +976,14 @@ 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 endpoint The endpoint that resulted in the contact being added
  *
  * \retval -1 failure
  * \retval 0 success
  */
 int ast_sip_location_add_contact(struct ast_sip_aor *aor, const char *uri,
-	struct timeval expiration_time, const char *path_info, const char *user_agent);
+	struct timeval expiration_time, const char *path_info, const char *user_agent,
+	struct ast_sip_endpoint *endpoint);
 
 /*!
  * \brief Update a contact
@@ -959,30 +1006,6 @@ int ast_sip_location_update_contact(struct ast_sip_contact *contact);
 int ast_sip_location_delete_contact(struct ast_sip_contact *contact);
 
 /*!
- * \brief Initialize domain aliases support on a sorcery instance
- *
- * \retval -1 failure
- * \retval 0 success
- */
-int ast_sip_initialize_sorcery_domain_alias(void);
-
-/*!
- * \brief Initialize authentication support on a sorcery instance
- *
- * \retval -1 failure
- * \retval 0 success
- */
-int ast_sip_initialize_sorcery_auth(void);
-
-/*!
- * \brief Destroy authentication support on a sorcery instance
- *
- * \retval -1 failure
- * \retval 0 success
- */
-int ast_sip_destroy_sorcery_auth(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
@@ -1018,26 +1041,6 @@ int ast_sip_dialog_setup_outbound_authentication(pjsip_dialog *dlg, const struct
 		ast_sip_dialog_outbound_auth_cb cb, void *user_data);
 
 /*!
- * \brief Initialize the distributor module
- *
- * The distributor module is responsible for taking an incoming
- * SIP message and placing it into the threadpool. Once in the threadpool,
- * the distributor will perform endpoint lookups and authentication, and
- * then distribute the message up the stack to any further modules.
- *
- * \retval -1 Failure
- * \retval 0 Success
- */
-int ast_sip_initialize_distributor(void);
-
-/*!
- * \brief Destruct the distributor module.
- *
- * Unregisters pjsip modules and cleans up any allocated resources.
- */
-void ast_sip_destroy_distributor(void);
-
-/*!
  * \brief Retrieves a reference to the artificial auth.
  *
  * \retval The artificial auth
@@ -1121,6 +1124,23 @@ struct ast_sip_endpoint *ast_sip_get_artificial_endpoint(void);
  */
 struct ast_taskprocessor *ast_sip_create_serializer(void);
 
+struct ast_serializer_shutdown_group;
+
+/*!
+ * \brief Create a new serializer for SIP tasks
+ * \since 13.5.0
+ *
+ * See \ref ast_threadpool_serializer for more information on serializers.
+ * SIP creates serializers so that tasks operating on similar data will run
+ * in sequence.
+ *
+ * \param shutdown_group Group shutdown controller. (NULL if no group association)
+ *
+ * \retval NULL Failure
+ * \retval non-NULL Newly-created serializer
+ */
+struct ast_taskprocessor *ast_sip_create_serializer_group(struct ast_serializer_shutdown_group *shutdown_group);
+
 /*!
  * \brief Set a serializer on a SIP dialog so requests and responses are automatically serialized
  *
@@ -1176,6 +1196,11 @@ int ast_sip_push_task(struct ast_taskprocessor *serializer, int (*sip_task)(void
  * cause a deadlock. If you are in a SIP servant thread, just call your function
  * in-line.
  *
+ * \warning \b Never hold locks that may be acquired by a SIP servant thread when
+ * calling this function. Doing so may cause a deadlock if all SIP servant threads
+ * are blocked waiting to acquire the lock while the thread holding the lock is
+ * waiting for a free SIP servant thread.
+ *
  * \param serializer The SIP serializer to which the task belongs. May be NULL.
  * \param sip_task The task to execute
  * \param task_data The parameter to pass to the task when it executes
@@ -1303,6 +1328,37 @@ int ast_sip_send_request(pjsip_tx_data *tdata, struct pjsip_dialog *dlg,
 	void (*callback)(void *token, pjsip_event *e));
 
 /*!
+ * \brief General purpose method for sending an Out-Of-Dialog SIP request
+ *
+ * This is a companion function for \ref ast_sip_create_request. The request
+ * created there can be passed to this function, though any request may be
+ * passed in.
+ *
+ * This will automatically set up handling outbound authentication challenges if
+ * they arrive.
+ *
+ * \param tdata The request to send
+ * \param endpoint Optional. If specified, the out-of-dialog request is sent to the endpoint.
+ * \param timeout.  If non-zero, after the timeout the transaction will be terminated
+ * and the callback will be called with the PJSIP_EVENT_TIMER type.
+ * \param token Data to be passed to the callback upon receipt of out-of-dialog response.
+ * \param callback Callback to be called upon receipt of out-of-dialog response.
+ *
+ * \retval 0 Success
+ * \retval -1 Failure (out-of-dialog callback will not be called.)
+ *
+ * \note Timeout processing:
+ * There are 2 timers associated with this request, PJSIP timer_b which is
+ * set globally in the "system" section of pjsip.conf, and the timeout specified
+ * on this call.  The timer that expires first (before normal completion) will
+ * cause the callback to be run with e->body.tsx_state.type = PJSIP_EVENT_TIMER.
+ * The timer that expires second is simply ignored and the callback is not run again.
+ */
+int ast_sip_send_out_of_dialog_request(pjsip_tx_data *tdata,
+	struct ast_sip_endpoint *endpoint, int timeout, void *token,
+	void (*callback)(void *token, pjsip_event *e));
+
+/*!
  * \brief General purpose method for creating a SIP response
  *
  * Its typical use would be to create responses for out of dialog
@@ -1326,6 +1382,11 @@ int ast_sip_create_response(const pjsip_rx_data *rdata, int st_code,
 /*!
  * \brief Send a response to an out of dialog request
  *
+ * Use this function sparingly, since this does not create a transaction
+ * within PJSIP. This means that if the request is retransmitted, it is
+ * your responsibility to detect this and not process the same request
+ * twice, and to send the same response for each retransmission.
+ *
  * \param res_addr The response address for this response
  * \param tdata The response to send
  * \param endpoint The ast_sip_endpoint associated with this response
@@ -1336,6 +1397,24 @@ int ast_sip_create_response(const pjsip_rx_data *rdata, int st_code,
 int ast_sip_send_response(pjsip_response_addr *res_addr, pjsip_tx_data *tdata, struct ast_sip_endpoint *sip_endpoint);
 
 /*!
+ * \brief Send a stateful response to an out of dialog request
+ *
+ * This creates a transaction within PJSIP, meaning that if the request
+ * that we are responding to is retransmitted, we will not attempt to
+ * re-handle the request.
+ *
+ * \param rdata The request that is being responded to
+ * \param tdata The response to send
+ * \param endpoint The ast_sip_endpoint associated with this response
+ *
+ * \since 13.4.0
+ *
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_sip_send_stateful_response(pjsip_rx_data *rdata, pjsip_tx_data *tdata, struct ast_sip_endpoint *sip_endpoint);
+
+/*!
  * \brief Determine if an incoming request requires authentication
  *
  * This calls into the registered authenticator's requires_authentication callback
@@ -1379,6 +1458,17 @@ int ast_sip_create_request_with_auth(const struct ast_sip_auth_vector *auths, pj
 		pjsip_transaction *tsx, pjsip_tx_data **new_request);
 
 /*!
+ * \brief Create a response to an authentication challenge
+ *
+ * This will call into an outbound authenticator's create_request_with_auth callback
+ * to create a new request with authentication credentials. See the create_request_with_auth_from_old
+ * callback in the \ref ast_sip_outbound_authenticator structure for details about
+ * the parameters and return values.
+ */
+int ast_sip_create_request_with_auth_from_old(const struct ast_sip_auth_vector *auths, pjsip_rx_data *challenge,
+		pjsip_tx_data *old_request, pjsip_tx_data **new_request);
+
+/*!
  * \brief Determine the endpoint that has sent a SIP message
  *
  * This will call into each of the registered endpoint identifiers'
@@ -1487,6 +1577,15 @@ void ast_copy_pj_str(char *dest, const pj_str_t *src, size_t size);
 struct ast_sip_endpoint *ast_pjsip_rdata_get_endpoint(pjsip_rx_data *rdata);
 
 /*!
+ * \brief Add 'user=phone' parameter to URI if enabled and user is a phone number.
+ *
+ * \param endpoint The endpoint to use for configuration
+ * \param pool The memory pool to allocate the parameter from
+ * \param uri The URI to check for user and to add parameter to
+ */
+void ast_sip_add_usereqphone(const struct ast_sip_endpoint *endpoint, pj_pool_t *pool, pjsip_uri *uri);
+
+/*!
  * \brief Retrieve any endpoints available to sorcery.
  *
  * \retval Endpoints available to sorcery, NULL if no endpoints found.
@@ -1594,14 +1693,9 @@ void ast_sip_report_req_no_support(struct ast_sip_endpoint *endpoint, pjsip_rx_d
  */
 void ast_sip_report_mem_limit(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata);
 
-void ast_sip_initialize_global_headers(void);
-void ast_sip_destroy_global_headers(void);
-
 int ast_sip_add_global_request_header(const char *name, const char *value, int replace);
 int ast_sip_add_global_response_header(const char *name, const char *value, int replace);
 
-int ast_sip_initialize_sorcery_global(void);
-
 /*!
  * \brief Retrieves the value associated with the given key.
  *
@@ -1718,7 +1812,7 @@ const char *ast_sip_auth_type_to_str(enum ast_sip_auth_type type);
  */
 int ast_sip_auths_to_str(const struct ast_sip_auth_vector *auths, char **buf);
 
-/*
+/*!
  * \brief AMI variable container
  */
 struct ast_sip_ami {
@@ -1947,6 +2041,28 @@ void ast_sip_unregister_supplement(struct ast_sip_supplement *supplement);
  */
 char *ast_sip_get_debug(void);
 
+/*!
+ * \brief Retrieve the global endpoint_identifier_order setting.
+ *
+ * Specifies the order by which endpoint identifiers should be regarded.
+ *
+ * \retval the global endpoint_identifier_order value
+ */
+char *ast_sip_get_endpoint_identifier_order(void);
+
+/*!
+ * \brief Retrieve the global default from user.
+ *
+ * This is the value placed in outbound requests' From header if there
+ * is no better option (such as an endpoint-configured from_user or
+ * caller ID number).
+ *
+ * \param[out] from_user The default from user
+ * \param size The buffer size of from_user
+ * \return nothing
+ */
+void ast_sip_get_default_from_user(char *from_user, size_t size);
+
 /*! \brief Determines whether the res_pjsip module is loaded */
 #define CHECK_PJSIP_MODULE_LOADED()				\
 	do {							\
@@ -1956,4 +2072,60 @@ char *ast_sip_get_debug(void);
 		}						\
 	} while(0)
 
+/*!
+ * \brief Retrieve the system keep alive interval setting.
+ *
+ * \retval the keep alive interval.
+ */
+unsigned int ast_sip_get_keep_alive_interval(void);
+
+/*!
+ * \brief Retrieve the system max initial qualify time.
+ *
+ * \retval the maximum initial qualify time.
+ */
+unsigned int ast_sip_get_max_initial_qualify_time(void);
+
+/*!
+ * \brief translate ast_sip_contact_status_type to character string.
+ *
+ * \retval the character string equivalent.
+ */
+
+const char *ast_sip_get_contact_status_label(const enum ast_sip_contact_status_type status);
+const char *ast_sip_get_contact_short_status_label(const enum ast_sip_contact_status_type status);
+
+/*!
+ * \brief Retrieve the local host address in IP form
+ *
+ * \param af The address family to retrieve
+ * \param addr A place to store the local host address
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ *
+ * \since 13.6.0
+ */
+int ast_sip_get_host_ip(int af, pj_sockaddr *addr);
+
+/*!
+ * \brief Retrieve the local host address in string form
+ *
+ * \param af The address family to retrieve
+ *
+ * \retval non-NULL success
+ * \retval NULL failure
+ *
+ * \since 13.6.0
+ *
+ * \note An empty string may be returned if the address family is valid but no local address exists
+ */
+const char *ast_sip_get_host_ip_string(int af);
+
+/*!
+ * \brief Return the size of the SIP threadpool's task queue
+ * \since 13.7.0
+ */
+long ast_sip_threadpool_queue_size(void);
+
 #endif /* _RES_PJSIP_H */
diff --git a/include/asterisk/res_pjsip_cli.h b/include/asterisk/res_pjsip_cli.h
index 44979b7..c253521 100644
--- a/include/asterisk/res_pjsip_cli.h
+++ b/include/asterisk/res_pjsip_cli.h
@@ -61,7 +61,7 @@ struct ast_sip_cli_formatter_entry {
 	/*! The callback used to print the details of the object. */
 	ao2_callback_fn *print_body;
 	/*! The function used to retrieve a container of all objects of this type. */
-	struct ao2_container *(* get_container)(void);
+	struct ao2_container *(* get_container)(const char *regex);
 	/*! The function used to iterate over a container of objects. */
 	int (* iterate)(void *container, ao2_callback_fn callback, void *args);
 	/*! The function used to retrieve a specific object from it's container. */
diff --git a/include/asterisk/res_pjsip_presence_xml.h b/include/asterisk/res_pjsip_presence_xml.h
index add5f89..deed090 100644
--- a/include/asterisk/res_pjsip_presence_xml.h
+++ b/include/asterisk/res_pjsip_presence_xml.h
@@ -17,14 +17,15 @@
  */
 
 /*!
- * \brief The length of the XML prolog when printing
- * presence or other XML in PJSIP.
+ * \brief Length of the XML prolog when printing presence or other XML in PJSIP.
  *
  * When calling any variant of pj_xml_print(), the documentation
  * claims that it will return -1 if the provided buffer is not
  * large enough. However, if the XML prolog is requested to be
- * printed, then the length of the XML prolog is returned upon
- * failure instead of -1.
+ * printed and the buffer is not large enough, then it will
+ * return -1 only if the buffer is not large enough to hold the
+ * XML prolog or return the length of the XML prolog on failure
+ * instead of -1.
  *
  * This constant is useful to check against when trying to determine
  * if printing XML succeeded or failed.
diff --git a/include/asterisk/res_pjsip_pubsub.h b/include/asterisk/res_pjsip_pubsub.h
index d32b246..c9b66dc 100644
--- a/include/asterisk/res_pjsip_pubsub.h
+++ b/include/asterisk/res_pjsip_pubsub.h
@@ -406,6 +406,16 @@ void ast_sip_subscription_get_remote_uri(struct ast_sip_subscription *sub, char
 const char *ast_sip_subscription_get_resource_name(struct ast_sip_subscription *sub);
 
 /*!
+ * \brief Get whether the subscription has been terminated or not.
+ *
+ * \param sub The subscription.
+ * \retval 0 not terminated.
+ * \retval 1 terminated.
+ * \since 13.4.0
+ */
+int ast_sip_subscription_is_terminated(const struct ast_sip_subscription *sub);
+
+/*!
  * \brief Get a header value for a subscription.
  *
  * For notifiers, the headers of the inbound SUBSCRIBE that started the dialog
@@ -674,6 +684,15 @@ const char *ast_sip_subscription_get_body_type(struct ast_sip_subscription *sub)
  */
 const char *ast_sip_subscription_get_body_subtype(struct ast_sip_subscription *sub);
 
+/*!
+ * \since 13.6.0
+ * \brief Alert the pubsub core that the subscription is ready for destruction
+ *
+ * \param sub The subscription that is complete
+ * \return Nothing
+ */
+void ast_sip_subscription_destroy(struct ast_sip_subscription *sub);
+
 /*! \brief Determines whether the res_pjsip_pubsub module is loaded */
 #define CHECK_PJSIP_PUBSUB_MODULE_LOADED()			\
 	do {							\
diff --git a/include/asterisk/res_pjsip_session.h b/include/asterisk/res_pjsip_session.h
index 30d325e..ddc87b0 100644
--- a/include/asterisk/res_pjsip_session.h
+++ b/include/asterisk/res_pjsip_session.h
@@ -77,6 +77,10 @@ struct ast_sip_session_media {
 	enum ast_sip_session_media_encryption encryption;
 	/*! \brief The media transport in use for this stream */
 	pj_str_t transport;
+	/*! \brief Scheduler ID for RTP keepalive */
+	int keepalive_sched_id;
+	/*! \brief Scheduler ID for RTP timeout */
+	int timeout_sched_id;
 	/*! \brief Stream is on hold */
 	unsigned int held:1;
 	/*! \brief Stream type this session media handles */
@@ -137,10 +141,14 @@ struct ast_sip_session {
 	struct ast_dsp *dsp;
 	/*! Whether the termination of the session should be deferred */
 	unsigned int defer_terminate:1;
+	/*! Termination requested while termination deferred */
+	unsigned int terminate_while_deferred:1;
 	/*! Deferred incoming re-invite */
 	pjsip_rx_data *deferred_reinvite;
 	/*! Current T.38 state */
 	enum ast_sip_session_t38state t38state;
+	/*! The AOR associated with this session */
+	struct ast_sip_aor *aor;
 };
 
 typedef int (*ast_sip_session_request_creation_cb)(struct ast_sip_session *session, pjsip_tx_data *tdata);
@@ -347,6 +355,12 @@ struct ast_sip_session_sdp_handler {
 	int (*apply_negotiated_sdp_stream)(struct ast_sip_session *session, struct ast_sip_session_media *session_media, const struct pjmedia_sdp_session *local, const struct pjmedia_sdp_media *local_stream,
 		const struct pjmedia_sdp_session *remote, const struct pjmedia_sdp_media *remote_stream);
 	/*!
+	 * \brief Stop a session_media created by this handler but do not destroy resources
+	 * \param session The session for which media is being stopped
+	 * \param session_media The media to destroy
+	 */
+	void (*stream_stop)(struct ast_sip_session_media *session_media);
+	/*!
 	 * \brief Destroy a session_media created by this handler
 	 * \param session The session for which media is being destroyed
 	 * \param session_media The media to destroy
@@ -434,11 +448,29 @@ struct ast_sip_session *ast_sip_session_create_outgoing(struct ast_sip_endpoint
 	struct ast_format_cap *req_caps);
 
 /*!
+ * \brief Terminate a session and, if possible, send the provided response code
+ *
+ * \param session The session to terminate
+ * \param response The response code to use for termination if possible
+ */
+void ast_sip_session_terminate(struct ast_sip_session *session, int response);
+
+/*!
  * \brief Defer local termination of a session until remote side terminates, or an amount of time passes
  *
  * \param session The session to defer termination on
+ *
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_sip_session_defer_termination(struct ast_sip_session *session);
+
+/*!
+ * \brief Cancel a pending deferred termination.
+ *
+ * \param session The session to cancel a deferred termination on.
  */
-void ast_sip_session_defer_termination(struct ast_sip_session *session);
+void ast_sip_session_defer_termination_cancel(struct ast_sip_session *session);
 
 /*!
  * \brief Register an SDP handler
diff --git a/include/asterisk/rtp_engine.h b/include/asterisk/rtp_engine.h
index db5cd34..80183ed 100644
--- a/include/asterisk/rtp_engine.h
+++ b/include/asterisk/rtp_engine.h
@@ -1,4 +1,4 @@
- /*
+/*
  * Asterisk -- An open source telephony toolkit.
  *
  * Copyright (C) 1999 - 2009, Digium, Inc.
@@ -78,14 +78,13 @@ extern "C" {
 #include "asterisk/stasis.h"
 #include "asterisk/vector.h"
 
-/* Maximum number of payloads supported */
-#if defined(LOW_MEMORY)
+/*! Maximum number of payload types RTP can support. */
 #define AST_RTP_MAX_PT 128
-#else
-#define AST_RTP_MAX_PT 196
-#endif
 
-/* Maximum number of generations */
+/*! First dynamic RTP payload type */
+#define AST_RTP_PT_FIRST_DYNAMIC 96
+
+/*! Maximum number of generations */
 #define AST_RED_MAX_GENERATION 5
 
 /*!
@@ -625,7 +624,7 @@ struct ast_rtp_glue {
 	enum ast_rtp_glue_result (*get_trtp_info)(struct ast_channel *chan, struct ast_rtp_instance **instance);
 	/*! Callback for updating the destination that the remote side should send RTP to */
 	int (*update_peer)(struct ast_channel *chan, struct ast_rtp_instance *instance, struct ast_rtp_instance *vinstance, struct ast_rtp_instance *tinstance, const struct ast_format_cap *cap, int nat_active);
-	/*! Callback for retrieving codecs that the channel can do.  Result returned in result_cap*/
+	/*! Callback for retrieving codecs that the channel can do.  Result returned in result_cap. */
 	void (*get_codec)(struct ast_channel *chan, struct ast_format_cap *result_cap);
 	/*! Linked list information */
 	AST_RWLIST_ENTRY(ast_rtp_glue) entry;
@@ -876,6 +875,40 @@ int ast_rtp_instance_write(struct ast_rtp_instance *instance, struct ast_frame *
 struct ast_frame *ast_rtp_instance_read(struct ast_rtp_instance *instance, int rtcp);
 
 /*!
+ * \brief Set the incoming source address of the remote endpoint that we are sending RTP to
+ *
+ * This sets the incoming source address the engine is sending RTP to. Usually this
+ * will be the same as the requested target address, however in the case where
+ * the engine "learns" the address (for instance, symmetric RTP enabled) this
+ * will then contain the learned address.
+ *
+ * \param instance The RTP instance to change the address on
+ * \param address Address to set it to
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+int ast_rtp_instance_set_incoming_source_address(struct ast_rtp_instance *instance,
+						 const struct ast_sockaddr *address);
+
+/*!
+ * \brief Set the requested target address of the remote endpoint
+ *
+ * This should always be the address of the remote endpoint. Consequently, this can differ
+ * from the address the engine is sending RTP to.  However, usually they will be the same
+ * except in some circumstances (for instance when the engine "learns" the address if
+ * symmetric RTP is enabled).
+ *
+ * \param instance The RTP instance to change the address on
+ * \param address Address to set it to
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+int ast_rtp_instance_set_requested_target_address(struct ast_rtp_instance *instance,
+						  const struct ast_sockaddr *address);
+
+/*!
  * \brief Set the address of the remote endpoint that we are sending RTP to
  *
  * \param instance The RTP instance to change the address on
@@ -895,7 +928,8 @@ struct ast_frame *ast_rtp_instance_read(struct ast_rtp_instance *instance, int r
  *
  * \since 1.8
  */
-int ast_rtp_instance_set_remote_address(struct ast_rtp_instance *instance, const struct ast_sockaddr *address);
+#define ast_rtp_instance_set_remote_address(instance, address) \
+	ast_rtp_instance_set_requested_target_address((instance), (address));
 
 /*!
  * \brief Set the address that we are expecting to receive RTP on
@@ -963,6 +997,32 @@ void ast_rtp_instance_get_local_address(struct ast_rtp_instance *instance, struc
 int ast_rtp_instance_get_and_cmp_local_address(struct ast_rtp_instance *instance, struct ast_sockaddr *address);
 
 /*!
+ * \brief Get the incoming source address of the remote endpoint
+ *
+ * This returns the remote address the engine is sending RTP to. Usually this
+ * will be the same as the requested target address, however in the case where
+ * the engine "learns" the address (for instance, symmetric RTP enabled) this
+ * will then contain the learned address.
+ *
+ * \param instance The instance that we want to get the incoming source address for
+ * \param address A structure to put the address into
+ */
+void ast_rtp_instance_get_incoming_source_address(struct ast_rtp_instance *instance, struct ast_sockaddr *address);
+
+/*!
+ * \brief Get the requested target address of the remote endpoint
+ *
+ * This returns the explicitly set address of a remote endpoint. Meaning this won't change unless
+ * specifically told to change. In most cases this should be the same as the incoming source
+ * address, except in cases where the engine "learns" the address in which case this and the
+ * incoming source address might differ.
+ *
+ * \param instance The instance that we want to get the requested target address for
+ * \param address A structure to put the address into
+ */
+void ast_rtp_instance_get_requested_target_address(struct ast_rtp_instance *instance, struct ast_sockaddr *address);
+
+/*!
  * \brief Get the address of the remote endpoint that we are sending RTP to
  *
  * \param instance The instance that we want to get the remote address for
@@ -980,7 +1040,20 @@ int ast_rtp_instance_get_and_cmp_local_address(struct ast_rtp_instance *instance
  *
  * \since 1.8
  */
-void ast_rtp_instance_get_remote_address(struct ast_rtp_instance *instance, struct ast_sockaddr *address);
+#define ast_rtp_instance_get_remote_address(instance, address) \
+	ast_rtp_instance_get_incoming_source_address((instance), (address));
+
+/*!
+ * \brief Get the requested target address of the remote endpoint and
+ *        compare it to the given address
+ *
+ * \param instance The instance that we want to get the remote address for
+ * \param address An initialized address that may be overwritten addresses differ
+ *
+ * \retval 0 address was not changed
+ * \retval 1 address was changed
+ */
+int ast_rtp_instance_get_and_cmp_requested_target_address(struct ast_rtp_instance *instance, struct ast_sockaddr *address);
 
 /*!
  * \brief Get the address of the remote endpoint that we are sending RTP to, comparing its address to another
@@ -1003,8 +1076,8 @@ void ast_rtp_instance_get_remote_address(struct ast_rtp_instance *instance, stru
  *
  * \since 1.8
  */
-
-int ast_rtp_instance_get_and_cmp_remote_address(struct ast_rtp_instance *instance, struct ast_sockaddr *address);
+#define ast_rtp_instance_get_and_cmp_remote_address(instance, address) \
+	ast_rtp_instance_get_and_cmp_requested_target_address((instance), (address));
 
 /*!
  * \brief Set the value of an RTP instance extended property
@@ -1344,7 +1417,7 @@ unsigned int ast_rtp_lookup_sample_rate2(int asterisk_format, struct ast_format
  * \code
  * struct ast_format_cap *astformats = ast_format_cap_alloc_nolock()
  * int nonastformats;
- * ast_rtp_codecs_payload_formats(&codecs, &astformats, &nonastformats);
+ * ast_rtp_codecs_payload_formats(&codecs, astformats, &nonastformats);
  * \endcode
  *
  * This retrieves all the formats known about in the codecs structure and puts the Asterisk ones in the integer
@@ -1375,6 +1448,7 @@ void ast_rtp_codecs_payload_formats(struct ast_rtp_codecs *codecs, struct ast_fo
  * \since 1.8
  */
 int ast_rtp_codecs_payload_code(struct ast_rtp_codecs *codecs, int asterisk_format, const struct ast_format *format, int code);
+
 /*!
  * \brief Search for a payload code in the ast_rtp_codecs structure
  *
@@ -2214,6 +2288,38 @@ void ast_rtp_publish_rtcp_message(struct ast_rtp_instance *rtp,
 		struct ast_rtp_rtcp_report *report,
 		struct ast_json *blob);
 
+/*!
+ * \brief Get the last RTP transmission time
+ *
+ * \param rtp The instance from which to get the last transmission time
+ * \return The last RTP transmission time
+ */
+time_t ast_rtp_instance_get_last_tx(const struct ast_rtp_instance *rtp);
+
+/*!
+ * \brief Set the last RTP transmission time
+ *
+ * \param rtp The instance on which to set the last transmission time
+ * \param time The last transmission time
+ */
+void ast_rtp_instance_set_last_tx(struct ast_rtp_instance *rtp, time_t time);
+
+/*
+ * \brief Get the last RTP reception time
+ *
+ * \param rtp The instance from which to get the last reception time
+ * \return The last RTP reception time
+ */
+time_t ast_rtp_instance_get_last_rx(const struct ast_rtp_instance *rtp);
+
+/*!
+ * \brief Set the last RTP reception time
+ *
+ * \param rtp The instance on which to set the last reception time
+ * \param time The last reception time
+ */
+void ast_rtp_instance_set_last_rx(struct ast_rtp_instance *rtp, time_t time);
+
 /*! \addtogroup StasisTopicsAndMessages
  * @{
  */
diff --git a/include/asterisk/sched.h b/include/asterisk/sched.h
index 67dbf21..b775095 100644
--- a/include/asterisk/sched.h
+++ b/include/asterisk/sched.h
@@ -170,6 +170,17 @@ void ast_sched_context_destroy(struct ast_sched_context *c);
 typedef int (*ast_sched_cb)(const void *data);
 #define AST_SCHED_CB(a) ((ast_sched_cb)(a))
 
+/*!
+ * \brief Clean all scheduled events with matching callback.
+ *
+ * \param con Scheduler Context
+ * \param match Callback to match
+ * \param cleanup_cb Callback to run
+ *
+ * \note The return of cleanup_cb is ignored. No events are rescheduled.
+ */
+void ast_sched_clean_by_callback(struct ast_sched_context *con, ast_sched_cb match, ast_sched_cb cleanup_cb);
+
 struct ast_cb_names {
 	int numassocs;
 	char *list[10];
diff --git a/include/asterisk/sem.h b/include/asterisk/sem.h
index 8f6356c..6d655d6 100644
--- a/include/asterisk/sem.h
+++ b/include/asterisk/sem.h
@@ -59,6 +59,11 @@ static force_inline int ast_sem_wait(struct ast_sem *sem)
 	return sem_wait(&sem->real_sem);
 }
 
+static force_inline int ast_sem_timedwait(struct ast_sem *sem, const struct timespec *abs_timeout)
+{
+	return sem_timedwait(&sem->real_sem, abs_timeout);
+}
+
 static force_inline int ast_sem_getvalue(struct ast_sem *sem, int *sval)
 {
 	return sem_getvalue(&sem->real_sem, sval);
@@ -137,6 +142,20 @@ int ast_sem_post(struct ast_sem *sem);
 int ast_sem_wait(struct ast_sem *sem);
 
 /*!
+ * \brief Decrements the semaphore, waiting until abs_timeout.
+ *
+ * If the semaphore's current value is zero, this function blocks until another
+ * thread posts (ast_sem_post()) to the semaphore (or is interrupted by a signal
+ * handler, which sets errno to EINTR).
+ *
+ * \param sem Semaphore to decrement.
+ *
+ * \return 0 on success.
+ * \return -1 on error, errno set to indicate error.
+ */
+int ast_sem_timedwait(struct ast_sem *sem, const struct timespec *abs_timeout);
+
+/*!
  * \brief Gets the current value of the semaphore.
  *
  * If threads are blocked on this semaphore, POSIX allows the return value to be
diff --git a/include/asterisk/sip_api.h b/include/asterisk/sip_api.h
index 2b8a3f2..fddac16 100644
--- a/include/asterisk/sip_api.h
+++ b/include/asterisk/sip_api.h
@@ -23,7 +23,6 @@
 extern "C" {
 #endif
 
-#include "asterisk/optional_api.h"
 #include "asterisk/config.h"
 
 #define AST_SIP_API_VERSION 1
diff --git a/include/asterisk/slin.h b/include/asterisk/slin.h
index 148ee09..9766374 100644
--- a/include/asterisk/slin.h
+++ b/include/asterisk/slin.h
@@ -62,7 +62,7 @@ static inline struct ast_frame *slin8_sample(void)
 {
 	static struct ast_frame f = {
 		.frametype = AST_FRAME_VOICE,
-		.datalen = sizeof(ex_slin8) * 2,
+		.datalen = sizeof(ex_slin8),
 		.samples = ARRAY_LEN(ex_slin8),
 		.mallocd = 0,
 		.offset = 0,
@@ -79,7 +79,7 @@ static inline struct ast_frame *slin16_sample(void)
 {
 	static struct ast_frame f = {
 		.frametype = AST_FRAME_VOICE,
-		.datalen = sizeof(ex_slin16) * 2,
+		.datalen = sizeof(ex_slin16),
 		.samples = ARRAY_LEN(ex_slin16),
 		.mallocd = 0,
 		.offset = 0,
diff --git a/include/asterisk/sorcery.h b/include/asterisk/sorcery.h
index 874dac2..027ec00 100644
--- a/include/asterisk/sorcery.h
+++ b/include/asterisk/sorcery.h
@@ -497,6 +497,136 @@ enum ast_sorcery_apply_result __ast_sorcery_apply_wizard_mapping(struct ast_sorc
 #define ast_sorcery_apply_wizard_mapping(sorcery, type, name, data, caching) \
 	__ast_sorcery_apply_wizard_mapping((sorcery), (type), AST_MODULE, (name), (data), (caching));
 
+
+/*!
+ * \brief Pre-defined locations to insert at
+ */
+enum ast_sorcery_wizard_position {
+	AST_SORCERY_WIZARD_POSITION_LAST = -1,
+	AST_SORCERY_WIZARD_POSITION_FIRST = 0,
+};
+
+/*!
+ * \brief Insert an additional object wizard mapping at a specific position
+ * in the wizard list
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param type Type of object to apply to
+ * \param module The name of the module, typically AST_MODULE
+ * \param name Name of the wizard to use
+ * \param data Data to be passed to wizard
+ * \param caching Wizard should cache
+ * \param position An index to insert to or one of ast_sorcery_wizard_position
+ *
+ * \return What occurred when applying the mapping
+ *
+ * \note This should be called *after* applying default mappings
+ * \note Wizards can be retrieved by using ast_sorcery_get_wizard_mapping_count
+ * and iterating over them using ast_sorcery_get_wizard_mapping.
+ *
+ * \since 13.4.0
+ */
+enum ast_sorcery_apply_result __ast_sorcery_insert_wizard_mapping(struct ast_sorcery *sorcery,
+	const char *type, const char *module, const char *name, const char *data,
+	unsigned int caching, int position);
+
+/*!
+ * \brief Insert an additional object wizard mapping at a specific position
+ * in the wizard list
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param type Type of object to apply to
+ * \param module The name of the module, typically AST_MODULE
+ * \param name Name of the wizard to use
+ * \param data Data to be passed to wizard
+ * \param position One of ast_sorcery_wizard_position
+ *
+ * \return What occurred when applying the mapping
+ *
+ * \note This should be called *after* applying default mappings
+ * \since 13.4.0
+ */
+#define ast_sorcery_insert_wizard_mapping(sorcery, type, name, data, caching, position) \
+	__ast_sorcery_insert_wizard_mapping((sorcery), (type), AST_MODULE, (name), (data), \
+		(caching), (position))
+
+/*!
+ * \brief Remove an object wizard mapping
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param type Type of object to remove from
+ * \param module The name of the module, typically AST_MODULE
+ * \param name The name of the wizard to remove
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ *
+ * \since 13.4.0
+ */
+int __ast_sorcery_remove_wizard_mapping(struct ast_sorcery *sorcery,
+	const char *type, const char *module, const char *name);
+
+/*!
+ * \brief Remove an object wizard mapping
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param type Type of object to remove from
+ * \param name The name of the wizard to remove
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ *
+ * \since 13.4.0
+ */
+#define ast_sorcery_remove_wizard_mapping(sorcery, type, name) \
+	__ast_sorcery_remove_wizard_mapping((sorcery), (type), AST_MODULE, (name))
+
+/*!
+ * \brief Return the number of wizards mapped to an object type
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param type Type of object
+ *
+ * \return Number of wizards or -1 for error
+ * \since 13.4.0
+ */
+int ast_sorcery_get_wizard_mapping_count(struct ast_sorcery *sorcery,
+	const char *type);
+
+/*!
+ * \brief By index, return a wizard mapped to an object type
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param type Type of object
+ * \param index Index of the wizard
+ * \param wizard A pointer to receive the wizard pointer
+ * \param data A pointer to receive the data pointer
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ *
+ * \warning The wizard will have its reference count bumped so you must
+ * call ao2_cleanup when you're done with it.
+ *
+ * \note The wizard and data returned are valid only for this object type
+ * and only while the wizard is applied to the object type.
+ *
+ * \since 13.4.0
+ */
+int ast_sorcery_get_wizard_mapping(struct ast_sorcery *sorcery,
+	const char *type, int index, struct ast_sorcery_wizard **wizard, void **data);
+
+/*!
+ * \brief Unregister an object type
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param type Type of object
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+int ast_sorcery_object_unregister(struct ast_sorcery *sorcery, const char *type);
+
 /*!
  * \brief Register an object type
  *
@@ -1160,6 +1290,15 @@ struct ast_sorcery_object_type *ast_sorcery_get_object_type(const struct ast_sor
 int ast_sorcery_is_object_field_registered(const struct ast_sorcery_object_type *object_type,
 		const char *field_name);
 
+/*!
+ * \brief Get the module that has opened the provided sorcery instance.
+ *
+ * \param sorcery The sorcery instance
+ *
+ * \return The module
+ */
+const char *ast_sorcery_get_module(const struct ast_sorcery *sorcery);
+
 #if defined(__cplusplus) || defined(c_plusplus)
 }
 #endif
diff --git a/include/asterisk/stasis.h b/include/asterisk/stasis.h
index 0b1b1e8..16b30cc 100644
--- a/include/asterisk/stasis.h
+++ b/include/asterisk/stasis.h
@@ -521,6 +521,17 @@ void stasis_publish_sync(struct stasis_subscription *sub, struct stasis_message
 typedef void (*stasis_subscription_cb)(void *data, struct stasis_subscription *sub, struct stasis_message *message);
 
 /*!
+ * \brief Stasis subscription callback function that does nothing.
+ *
+ * \note This callback should be used for events are not directly processed, but need
+ * to be generated so data can be retrieved from cache later.  Subscriptions with this
+ * callback can be released with \ref stasis_unsubscribe, even during module unload.
+ *
+ * \since 13.5
+ */
+void stasis_subscription_cb_noop(void *data, struct stasis_subscription *sub, struct stasis_message *message);
+
+/*!
  * \brief Create a subscription.
  *
  * In addition to being AO2 managed memory (requiring an ao2_cleanup() to free
diff --git a/include/asterisk/stasis_app.h b/include/asterisk/stasis_app.h
index e472754..f2b07e0 100644
--- a/include/asterisk/stasis_app.h
+++ b/include/asterisk/stasis_app.h
@@ -92,6 +92,21 @@ struct ao2_container *stasis_app_get_all(void);
 int stasis_app_register(const char *app_name, stasis_app_cb handler, void *data);
 
 /*!
+ * \brief Register a new Stasis application that receives all Asterisk events.
+ *
+ * If an application is already registered with the given name, the old
+ * application is sent a 'replaced' message and unregistered.
+ *
+ * \param app_name Name of this application.
+ * \param handler Callback for application messages.
+ * \param data Data blob to pass to the callback. Must be AO2 managed.
+ *
+ * \return 0 for success
+ * \return -1 for error.
+ */
+int stasis_app_register_all(const char *app_name, stasis_app_cb handler, void *data);
+
+/*!
  * \brief Unregister a Stasis application.
  * \param app_name Name of the application to unregister.
  */
@@ -245,8 +260,7 @@ enum stasis_app_user_event_res {
  * \param event_name Name of the Userevent.
  * \param source_uris URIs for the source objects to attach to event.
  * \param sources_count Array size of source_uris.
- * \param userevent_data Custom parameters for the user event
- * \param userevents_count Array size of userevent_data
+ * \param json_variables event blob variables.
  *
  * \return \ref stasis_app_user_event_res return code.
  */
@@ -487,6 +501,17 @@ void stasis_app_control_clear_roles(struct stasis_app_control *control);
 int stasis_app_control_continue(struct stasis_app_control *control, const char *context, const char *extension, int priority);
 
 /*!
+ * \brief Redirect a channel in \c res_stasis to a particular endpoint
+ *
+ * \param control Control for \c res_stasis
+ * \param endpoint The endpoint transfer string where the channel should be sent to
+ *
+ * \return 0 for success
+ * \return -1 for error
+ */
+int stasis_app_control_redirect(struct stasis_app_control *control, const char *endpoint);
+
+/*!
  * \brief Indicate ringing to the channel associated with this control.
  *
  * \param control Control for \c res_stasis.
diff --git a/include/asterisk/stasis_endpoints.h b/include/asterisk/stasis_endpoints.h
index 1d56a8f..539f270 100644
--- a/include/asterisk/stasis_endpoints.h
+++ b/include/asterisk/stasis_endpoints.h
@@ -119,6 +119,12 @@ void ast_endpoint_blob_publish(struct ast_endpoint *endpoint, struct stasis_mess
 struct stasis_message_type *ast_endpoint_state_type(void);
 
 /*!
+ * \brief Message type for endpoint contact state changes.
+ * \since 13.5
+ */
+struct stasis_message_type *ast_endpoint_contact_state_type(void);
+
+/*!
  * \brief Message type for \ref ast_endpoint_snapshot.
  * \since 12
  */
diff --git a/include/asterisk/statsd.h b/include/asterisk/statsd.h
index 8e5e2f9..4cbd213 100644
--- a/include/asterisk/statsd.h
+++ b/include/asterisk/statsd.h
@@ -29,7 +29,12 @@
 #include "asterisk/optional_api.h"
 
 /*! An instantaneous measurement of a value. */
-#define AST_STATSD_GUAGE "g"
+#define AST_STATSD_GAUGE "g"
+/*!
+ * Embarrassingly, gauge was misspelled for quite some time.
+ * \deprecated You should spell gauge correctly.
+ */
+#define AST_STATSD_GUAGE AST_STATSD_GAUGE
 /*! A change in a value. */
 #define AST_STATSD_COUNTER "c"
 /*! Measure of milliseconds. */
@@ -42,8 +47,46 @@
 /*!
  * \brief Send a stat to the configured statsd server.
  *
- * The is the most flexible function for sending a message to the statsd server,
- * but also the least easy to use. See ast_statsd_log() or
+ * This function uses a character argument for value instead of
+ * an intmax_t argument. This is designed to be simpler to use for
+ * updating a current value rather than resetting it.
+ *
+ * \param metric_name String (UTF-8) name of the metric.
+ * \param type_str Type of metric to send.
+ * \param value Value to send.
+ * \param sample_rate Percentage of samples to send.
+ * \since 13
+ */
+AST_OPTIONAL_API(void, ast_statsd_log_string, (const char *metric_name,
+	const char *metric_type, const char *value, double sample_rate), {});
+
+/*!
+ * \brief Send a stat to the configured statsd server.
+ * \since 13.7.0
+ *
+ * This is the most flexible function for sending a message to the statsd
+ * server. In addition to allowing the string value and sample rate to be specified,
+ * the metric_name can be formed as a printf style string with variable
+ * arguments.
+ *
+ * \param metric_name Format string (UTF-8) specifying the name of the metric.
+ * \param metric_type Type of metric to send.
+ * \param value Value to send.
+ * \param sample_rate Percentage of samples to send.
+ *
+ * Example Usage:
+ * \code
+ *     ast_statsd_log_string_va(AST_STATSD_GAUGE, "+1", 1.0, "endpoints.states.%s", state_name);
+ * \endcode
+ */
+AST_OPTIONAL_API_ATTR(void, format(printf, 1, 5), ast_statsd_log_string_va,
+	(const char *metric_name, const char *metric_type, const char *value, double sample_rate, ...), {});
+
+/*!
+ * \brief Send a stat to the configured statsd server.
+ *
+ * The is nearly the most flexible function for sending a message to the statsd
+ * server, but also the least easy to use. See ast_statsd_log() or
  * ast_statsd_log_sample() for a slightly more convenient interface.
  *
  * \param metric_name String (UTF-8) name of the metric.
@@ -57,6 +100,28 @@ AST_OPTIONAL_API(void, ast_statsd_log_full, (const char *metric_name,
 
 /*!
  * \brief Send a stat to the configured statsd server.
+ * \since 13.7.0
+ *
+ * This is the most flexible function for sending a message to the statsd
+ * server. In addition to allowing the value and sample rate to be specified,
+ * the metric_name can be formed as a printf style string with variable
+ * arguments.
+ *
+ * \param metric_name Format string (UTF-8) specifying the name of the metric.
+ * \param metric_type Type of metric to send.
+ * \param value Value to send.
+ * \param sample_rate Percentage of samples to send.
+ *
+ * Example Usage:
+ * \code
+ *     ast_statsd_log_full_va(AST_STATSD_TIMER, rtt, 1.0, "endpoint.%s.rtt", endpoint_name);
+ * \endcode
+ */
+AST_OPTIONAL_API_ATTR(void, format(printf, 1, 5), ast_statsd_log_full_va,
+	(const char *metric_name, const char *metric_type, intmax_t value, double sample_rate, ...), {});
+
+/*!
+ * \brief Send a stat to the configured statsd server.
  * \param metric_name String (UTF-8) name of the metric.
  * \param metric_type Type of metric to send.
  * \param value Value to send.
diff --git a/include/asterisk/strings.h b/include/asterisk/strings.h
index 79a2e49..af5ae6c 100644
--- a/include/asterisk/strings.h
+++ b/include/asterisk/strings.h
@@ -310,6 +310,60 @@ char *ast_unescape_semicolon(char *s);
 char *ast_unescape_c(char *s);
 
 /*!
+ * \brief Escape the 'to_escape' characters in the given string.
+ *
+ * \note The given output buffer will contain a truncated escaped
+ * version of the source string if the given buffer is not large
+ * enough.
+ *
+ * \param dest the escaped string
+ * \param s the source string to escape
+ * \param size The size of the destination buffer
+ * \param to_escape an array of characters to escape
+ *
+ * \return Pointer to the destination.
+ */
+char *ast_escape(char *dest, const char *s, size_t size, const char *to_escape);
+
+/*!
+ * \brief Escape standard 'C' sequences in the given string.
+ *
+ * \note The given output buffer will contain a truncated escaped
+ * version of the source string if the given buffer is not large
+ * enough.
+ *
+ * \param dest the escaped string
+ * \param s the source string to escape
+ * \param size The size of the destination buffer
+ *
+ * \return Pointer to the escaped string.
+ */
+char *ast_escape_c(char *dest, const char *s, size_t size);
+
+/*!
+ * \brief Escape the 'to_escape' characters in the given string.
+ *
+ * \note Caller is responsible for freeing the returned string
+ *
+ * \param s the source string to escape
+ * \param to_escape an array of characters to escape
+ *
+ * \return Pointer to the escaped string or NULL.
+ */
+char *ast_escape_alloc(const char *s, const char *to_escape);
+
+/*!
+ * \brief Escape standard 'C' sequences in the given string.
+ *
+ * \note Caller is responsible for freeing the returned string
+ *
+ * \param s the source string to escape
+ *
+ * \return Pointer to the escaped string or NULL.
+ */
+char *ast_escape_c_alloc(const char *s);
+
+/*!
   \brief Size-limited null-terminating string copy.
   \param dst The destination buffer.
   \param src The source string
@@ -1233,7 +1287,8 @@ static force_inline char *attribute_pure ast_str_to_upper(char *str)
  * \retval AO2 container for strings
  * \retval NULL if allocation failed
  */
-struct ao2_container *ast_str_container_alloc_options(enum ao2_container_opts opts, int buckets);
+//struct ao2_container *ast_str_container_alloc_options(enum ao2_container_opts opts, int buckets);
+struct ao2_container *ast_str_container_alloc_options(enum ao2_alloc_opts opts, int buckets);
 
 /*!
  * \since 12
diff --git a/include/asterisk/syslog.h b/include/asterisk/syslog.h
index a0dc9e9..45d351a 100644
--- a/include/asterisk/syslog.h
+++ b/include/asterisk/syslog.h
@@ -28,6 +28,8 @@
 extern "C" {
 #endif
 
+#define ASTNUMLOGLEVELS 32
+
 /*!
  * \since 1.8
  * \brief Maps a syslog facility name from a string to a syslog facility
diff --git a/include/asterisk/taskprocessor.h b/include/asterisk/taskprocessor.h
index f16f144..6ebf072 100644
--- a/include/asterisk/taskprocessor.h
+++ b/include/asterisk/taskprocessor.h
@@ -56,6 +56,8 @@
 
 struct ast_taskprocessor;
 
+#define AST_TASKPROCESSOR_HIGH_WATER_LEVEL 500
+
 /*!
  * \brief ast_tps_options for specification of taskprocessor options
  *
@@ -262,4 +264,10 @@ int ast_taskprocessor_is_task(struct ast_taskprocessor *tps);
  */
 const char *ast_taskprocessor_name(struct ast_taskprocessor *tps);
 
+/*!
+ * \brief Return the current size of the taskprocessor queue
+ * \since 13.7.0
+ */
+long ast_taskprocessor_size(struct ast_taskprocessor *tps);
+
 #endif /* __AST_TASKPROCESSOR_H__ */
diff --git a/include/asterisk/tcptls.h b/include/asterisk/tcptls.h
index 0e8d9d0..e1a632c 100644
--- a/include/asterisk/tcptls.h
+++ b/include/asterisk/tcptls.h
@@ -86,7 +86,15 @@ enum ast_ssl_flags {
 	/*! Use SSLv3 for outgoing client connections */
 	AST_SSL_SSLV3_CLIENT = (1 << 4),
 	/*! Use TLSv1 for outgoing client connections */
-	AST_SSL_TLSV1_CLIENT = (1 << 5)
+	AST_SSL_TLSV1_CLIENT = (1 << 5),
+	/*! Use server cipher order instead of the client order */
+	AST_SSL_SERVER_CIPHER_ORDER = (1 << 6),
+	/*! Disable TLSv1 support */
+	AST_SSL_DISABLE_TLSV1 = (1 << 7),
+	/*! Disable TLSv1.1 support */
+	AST_SSL_DISABLE_TLSV11 = (1 << 8),
+	/*! Disable TLSv1.2 support */
+	AST_SSL_DISABLE_TLSV12 = (1 << 9),
 };
 
 struct ast_tls_config {
diff --git a/include/asterisk/term.h b/include/asterisk/term.h
index 18d743b..fac36eb 100644
--- a/include/asterisk/term.h
+++ b/include/asterisk/term.h
@@ -67,8 +67,8 @@ extern "C" {
 #define COLORIZE_FMT	"%s%s%s"
 #define COLORIZE(fg, bg, str)	ast_term_color(fg,bg),str,ast_term_reset()
 /*! \brief Maximum number of characters needed for a color escape sequence,
- *         plus a null char */
-#define AST_TERM_MAX_ESCAPE_CHARS   12
+ *         and another one for a trailing reset, plus a null char */
+#define AST_TERM_MAX_ESCAPE_CHARS   23
 #define AST_TERM_MAX_ROTATING_BUFFERS	15
 
 /*! \brief Colorize a specified string by adding terminal color codes
diff --git a/include/asterisk/test.h b/include/asterisk/test.h
index 2ea8332..49731fe 100644
--- a/include/asterisk/test.h
+++ b/include/asterisk/test.h
@@ -224,13 +224,22 @@ struct ast_test_info {
 	/*!
 	 * \brief test category
 	 *
+	 * \details
 	 * Tests are categorized in a directory tree style hierarchy.  It is expected that
 	 * this string have both a leading and trailing forward slash ('/').
 	 */
 	const char *category;
-	/*! \brief optional short summary of test */
+	/*!
+	 * \brief Short summary of test
+	 *
+	 * \note The summary must not end with a newline.
+	 */
 	const char *summary;
-	/*! \brief optional brief detailed description of test */
+	/*!
+	 * \brief More detailed description of test
+	 *
+	 * \note The description must not end with a newline.
+	 */
 	const char *description;
 };
 
@@ -389,6 +398,28 @@ int __ast_test_status_update(const char *file, const char *func, int line, struc
 		}							\
 	} while(0)
 
+/*!
+ * \brief Check a test condition, report error and goto cleanup label if failed.
+ *
+ * \since 13.4.0
+ *
+ * This macro evaluates \a condition. If the condition evaluates to true (non-zero),
+ * nothing happens. If it evaluates to false (zero), then the failure is printed
+ * using \ref ast_test_status_update, the variable \a rc_variable is set to AST_TEST_FAIL,
+ * and a goto to \a cleanup_label is executed.
+ *
+ * \param test Currently executing test
+ * \param condition Boolean condition to check.
+ * \param rc_variable Variable to receive AST_TEST_FAIL.
+ * \param cleanup_label The label to go to on failure.
+ */
+#define ast_test_validate_cleanup(test, condition, rc_variable, cleanup_label) ({ \
+	if (!(condition)) {	\
+		ast_test_status_update((test), "%s: %s\n", "Condition failed", #condition); \
+		rc_variable = AST_TEST_FAIL; \
+		goto cleanup_label; \
+	} \
+})
 
 #endif /* TEST_FRAMEWORK */
 #endif /* _AST_TEST_H */
diff --git a/include/asterisk/threadpool.h b/include/asterisk/threadpool.h
index e1e7727..0f360c7 100644
--- a/include/asterisk/threadpool.h
+++ b/include/asterisk/threadpool.h
@@ -195,6 +195,44 @@ int ast_threadpool_push(struct ast_threadpool *pool, int (*task)(void *data), vo
  */
 void ast_threadpool_shutdown(struct ast_threadpool *pool);
 
+struct ast_serializer_shutdown_group;
+
+/*!
+ * \brief Create a serializer group shutdown control object.
+ * \since 13.5.0
+ *
+ * \return ao2 object to control shutdown of a serializer group.
+ */
+struct ast_serializer_shutdown_group *ast_serializer_shutdown_group_alloc(void);
+
+/*!
+ * \brief Wait for the serializers in the group to shutdown with timeout.
+ * \since 13.5.0
+ *
+ * \param shutdown_group Group shutdown controller. (Returns 0 immediately if NULL)
+ * \param timeout Number of seconds to wait for the serializers in the group to shutdown.
+ *     Zero if the timeout is disabled.
+ *
+ * \return Number of seriaizers that did not get shutdown within the timeout.
+ */
+int ast_serializer_shutdown_group_join(struct ast_serializer_shutdown_group *shutdown_group, int timeout);
+
+/*!
+ * \brief Get the threadpool serializer currently associated with this thread.
+ * \since 14.0.0
+ *
+ * \note The returned pointer is valid while the serializer
+ * thread is running.
+ *
+ * \note Use ao2_ref() on serializer if you are going to keep it
+ * for another thread.  To unref it you must then use
+ * ast_taskprocessor_unreference().
+ *
+ * \retval serializer on success.
+ * \retval NULL on error or no serializer associated with the thread.
+ */
+struct ast_taskprocessor *ast_threadpool_serializer_get_current(void);
+
 /*!
  * \brief Serialized execution of tasks within a \ref ast_threadpool.
  *
@@ -218,9 +256,46 @@ void ast_threadpool_shutdown(struct ast_threadpool *pool);
  *
  * \param name Name of the serializer. (must be unique)
  * \param pool \ref ast_threadpool for execution.
+ *
  * \return \ref ast_taskprocessor for enqueuing work.
  * \return \c NULL on error.
  */
 struct ast_taskprocessor *ast_threadpool_serializer(const char *name, struct ast_threadpool *pool);
 
+/*!
+ * \brief Serialized execution of tasks within a \ref ast_threadpool.
+ * \since 13.5.0
+ *
+ * A \ref ast_taskprocessor with the same contract as a default taskprocessor
+ * (tasks execute serially) except instead of executing out of a dedicated
+ * thread, execution occurs in a thread from a \ref ast_threadpool. Think of it
+ * as a lightweight thread.
+ *
+ * While it guarantees that each task will complete before executing the next,
+ * there is no guarantee as to which thread from the \c pool individual tasks
+ * will execute. This normally only matters if your code relys on thread
+ * specific information, such as thread locals.
+ *
+ * Use ast_taskprocessor_unreference() to dispose of the returned \ref
+ * ast_taskprocessor.
+ *
+ * Only a single taskprocessor with a given name may exist. This function will fail
+ * if a taskprocessor with the given name already exists.
+ *
+ * \param name Name of the serializer. (must be unique)
+ * \param pool \ref ast_threadpool for execution.
+ * \param shutdown_group Group shutdown controller. (NULL if no group association)
+ *
+ * \return \ref ast_taskprocessor for enqueuing work.
+ * \return \c NULL on error.
+ */
+struct ast_taskprocessor *ast_threadpool_serializer_group(const char *name,
+	struct ast_threadpool *pool, struct ast_serializer_shutdown_group *shutdown_group);
+
+/*!
+ * \brief Return the size of the threadpool's task queue
+ * \since 13.7.0
+ */
+long ast_threadpool_queue_size(struct ast_threadpool *pool);
+
 #endif /* ASTERISK_THREADPOOL_H */
diff --git a/include/asterisk/threadstorage.h b/include/asterisk/threadstorage.h
index e3ece8b..4e61f42 100644
--- a/include/asterisk/threadstorage.h
+++ b/include/asterisk/threadstorage.h
@@ -64,6 +64,9 @@ struct ast_threadstorage {
 void __ast_threadstorage_object_add(void *key, size_t len, const char *file, const char *function, unsigned int line);
 void __ast_threadstorage_object_remove(void *key);
 void __ast_threadstorage_object_replace(void *key_old, void *key_new, size_t len);
+#define THREADSTORAGE_RAW_CLEANUP(v) {}
+#else
+#define THREADSTORAGE_RAW_CLEANUP NULL
 #endif /* defined(DEBUG_THREADLOCALS) */
 
 /*!
@@ -84,6 +87,8 @@ void __ast_threadstorage_object_replace(void *key_old, void *key_new, size_t len
 	AST_THREADSTORAGE_CUSTOM_SCOPE(name, NULL, ast_free_ptr,) 
 #define AST_THREADSTORAGE_EXTERNAL(name) \
 	extern struct ast_threadstorage name
+#define AST_THREADSTORAGE_RAW(name) \
+	AST_THREADSTORAGE_CUSTOM_SCOPE(name, NULL, THREADSTORAGE_RAW_CLEANUP,)
 
 /*!
  * \brief Define a thread storage variable, with custom initialization and cleanup
@@ -216,4 +221,42 @@ void *__ast_threadstorage_get(struct ast_threadstorage *ts, size_t init_size, co
 #define ast_threadstorage_get(ts, init_size) __ast_threadstorage_get(ts, init_size, __FILE__, __PRETTY_FUNCTION__, __LINE__)
 #endif /* defined(DEBUG_THREADLOCALS) */
 
+/*!
+ * \brief Retrieve a raw pointer from threadstorage.
+ * \param ts Threadstorage object to operate on.
+ *
+ * \return A pointer associated with the current thread, NULL
+ * if no pointer is associated yet.
+ *
+ * \note This should only be used on threadstorage declared
+ * by AST_THREADSTORAGE_RAW unless you really know what
+ * you are doing.
+ */
+AST_INLINE_API(
+void *ast_threadstorage_get_ptr(struct ast_threadstorage *ts),
+{
+	pthread_once(&ts->once, ts->key_init);
+	return pthread_getspecific(ts->key);
+}
+)
+
+/*!
+ * \brief Set a raw pointer from threadstorage.
+ * \param ts Threadstorage object to operate on.
+ *
+ * \retval 0 Success
+ * \retval non-zero Failure
+ *
+ * \note This should only be used on threadstorage declared
+ * by AST_THREADSTORAGE_RAW unless you really know what
+ * you are doing.
+ */
+AST_INLINE_API(
+int ast_threadstorage_set_ptr(struct ast_threadstorage *ts, void *ptr),
+{
+	pthread_once(&ts->once, ts->key_init);
+	return pthread_setspecific(ts->key, ptr);
+}
+)
+
 #endif /* ASTERISK_THREADSTORAGE_H */
diff --git a/include/asterisk/translate.h b/include/asterisk/translate.h
index e8e4c02..e2a4f30 100644
--- a/include/asterisk/translate.h
+++ b/include/asterisk/translate.h
@@ -223,6 +223,14 @@ struct ast_trans_pvt {
 	struct ast_trans_pvt *next; /*!< next in translator chain */
 	struct timeval nextin;
 	struct timeval nextout;
+	/*! If a translation path using a format with attributes requires the output
+	 * to be a specific set of attributes, this variable will be set describing
+	 * those attributes to the translator. Otherwise, the translator must choose
+	 * a set of format attributes for the destination that preserves the quality
+	 * of the audio in the best way possible. For example with the Opus Codec,
+	 * explicit_dst contains an attribute which describes whether both parties
+	 * want to do forward-error correction (FEC). */
+	struct ast_format *explicit_dst;
 };
 
 /*! \brief generic frameout function */
diff --git a/include/asterisk/utils.h b/include/asterisk/utils.h
index a0e7cb6..c7a4737 100644
--- a/include/asterisk/utils.h
+++ b/include/asterisk/utils.h
@@ -25,7 +25,6 @@
 
 #include "asterisk/network.h"
 
-#include <execinfo.h>
 #include <time.h>	/* we want to override localtime_r */
 #include <unistd.h>
 #include <string.h>
@@ -485,6 +484,32 @@ long int ast_random(void);
  */
 #define ast_random_double() (((double)ast_random()) / RAND_MAX)
 
+/*!
+ * \brief DEBUG_CHAOS returns failure randomly
+ *
+ * DEBUG_CHAOS_RETURN(failure); can be used to fake
+ * failure of functions such as memory allocation,
+ * for the purposes of testing failure handling.
+ */
+#ifdef DEBUG_CHAOS
+#ifndef DEBUG_CHAOS_ALLOC_CHANCE
+#define DEBUG_CHAOS_ALLOC_CHANCE 100000
+#endif
+/* Could #define DEBUG_CHAOS_ENABLE ast_fully_booted */
+#ifndef DEBUG_CHAOS_ENABLE
+#define DEBUG_CHAOS_ENABLE 1
+#endif
+#define DEBUG_CHAOS_RETURN(CHANCE, FAILURE) \
+	do { \
+		if ((DEBUG_CHAOS_ENABLE) && (ast_random() % CHANCE == 0)) { \
+			return FAILURE; \
+		} \
+	} while (0)
+#else
+#define DEBUG_CHAOS_RETURN(c,f)
+#endif
+
+
 #ifndef __AST_DEBUG_MALLOC
 #define ast_std_malloc malloc
 #define ast_std_calloc calloc
@@ -500,26 +525,13 @@ long int ast_random(void);
 #define ast_free free
 #define ast_free_ptr ast_free
 
-/*
- * This buffer is in static memory. We never intend to read it,
- * nor do we care about multiple threads writing to it at the
- * same time. We only want to know if we're recursing too deep
- * already. 60 entries should be more than enough.  Function
- * call depth rarely exceeds 20 or so.
- */
-#define _AST_MEM_BACKTRACE_BUFLEN 60
-extern void *_ast_mem_backtrace_buffer[_AST_MEM_BACKTRACE_BUFLEN];
-
-/*
- * Ok, this sucks. But if we're already out of mem, we don't
- * want the logger to create infinite recursion (and a crash).
- */
+#if defined(AST_IN_CORE)
 #define MALLOC_FAILURE_MSG \
-	do { \
-		if (backtrace(_ast_mem_backtrace_buffer, _AST_MEM_BACKTRACE_BUFLEN) < _AST_MEM_BACKTRACE_BUFLEN) { \
-			ast_log(LOG_ERROR, "Memory Allocation Failure in function %s at line %d of %s\n", func, lineno, file); \
-		} \
-	} while (0)
+	ast_log_safe(LOG_ERROR, "Memory Allocation Failure in function %s at line %d of %s\n", func, lineno, file)
+#else
+#define MALLOC_FAILURE_MSG \
+	ast_log(LOG_ERROR, "Memory Allocation Failure in function %s at line %d of %s\n", func, lineno, file)
+#endif
 
 /*!
  * \brief A wrapper for malloc()
@@ -537,6 +549,8 @@ void * attribute_malloc _ast_malloc(size_t len, const char *file, int lineno, co
 {
 	void *p;
 
+	DEBUG_CHAOS_RETURN(DEBUG_CHAOS_ALLOC_CHANCE, NULL);
+
 	if (!(p = malloc(len))) {
 		MALLOC_FAILURE_MSG;
 	}
@@ -561,6 +575,8 @@ void * attribute_malloc _ast_calloc(size_t num, size_t len, const char *file, in
 {
 	void *p;
 
+	DEBUG_CHAOS_RETURN(DEBUG_CHAOS_ALLOC_CHANCE, NULL);
+
 	if (!(p = calloc(num, len))) {
 		MALLOC_FAILURE_MSG;
 	}
@@ -598,6 +614,8 @@ void * attribute_malloc _ast_realloc(void *p, size_t len, const char *file, int
 {
 	void *newp;
 
+	DEBUG_CHAOS_RETURN(DEBUG_CHAOS_ALLOC_CHANCE, NULL);
+
 	if (!(newp = realloc(p, len))) {
 		MALLOC_FAILURE_MSG;
 	}
@@ -626,6 +644,8 @@ char * attribute_malloc _ast_strdup(const char *str, const char *file, int linen
 {
 	char *newstr = NULL;
 
+	DEBUG_CHAOS_RETURN(DEBUG_CHAOS_ALLOC_CHANCE, NULL);
+
 	if (str) {
 		if (!(newstr = strdup(str))) {
 			MALLOC_FAILURE_MSG;
@@ -656,6 +676,8 @@ char * attribute_malloc _ast_strndup(const char *str, size_t len, const char *fi
 {
 	char *newstr = NULL;
 
+	DEBUG_CHAOS_RETURN(DEBUG_CHAOS_ALLOC_CHANCE, NULL);
+
 	if (str) {
 		if (!(newstr = strndup(str, len))) {
 			MALLOC_FAILURE_MSG;
@@ -697,6 +719,8 @@ int _ast_vasprintf(char **ret, const char *file, int lineno, const char *func, c
 {
 	int res;
 
+	DEBUG_CHAOS_RETURN(DEBUG_CHAOS_ALLOC_CHANCE, -1);
+
 	if ((res = vasprintf(ret, fmt, ap)) == -1) {
 		MALLOC_FAILURE_MSG;
 	}
@@ -792,7 +816,7 @@ int ast_safe_mkdir(const char *base_path, const char *path, int mode);
  * \param a the array to bound check
  * \return 0 if value out of bounds, otherwise true (non-zero)
  */
-#define ARRAY_IN_BOUNDS(v, a) IN_BOUNDS(v, 0, ARRAY_LEN(a) - 1)
+#define ARRAY_IN_BOUNDS(v, a) IN_BOUNDS((int) (v), 0, ARRAY_LEN(a) - 1)
 
 /* Definition for Digest authorization */
 struct ast_http_digest {
@@ -1005,12 +1029,27 @@ char *ast_utils_which(const char *binary, char *fullpath, size_t fullpath_size);
  * }
  * \endcode
  */
-#define RAII_VAR(vartype, varname, initval, dtor) \
-    /* Prototype needed due to http://gcc.gnu.org/bugzilla/show_bug.cgi?id=36774 */ \
-    auto void _dtor_ ## varname (vartype * v); \
-    void _dtor_ ## varname (vartype * v) { dtor(*v); } \
+
+#if defined(__clang__)
+typedef void (^_raii_cleanup_block_t)(void);
+static inline void _raii_cleanup_block(_raii_cleanup_block_t *b) { (*b)(); }
+
+#define RAII_VAR(vartype, varname, initval, dtor)                                                                \
+    _raii_cleanup_block_t _raii_cleanup_ ## varname __attribute__((cleanup(_raii_cleanup_block),unused)) = NULL; \
+    __block vartype varname = initval;                                                                           \
+    _raii_cleanup_ ## varname = ^{ {(void)dtor(varname);} }
+
+#elif defined(__GNUC__)
+
+#define RAII_VAR(vartype, varname, initval, dtor)                              \
+    auto void _dtor_ ## varname (vartype * v);                                 \
+    void _dtor_ ## varname (vartype * v) { dtor(*v); }                         \
     vartype varname __attribute__((cleanup(_dtor_ ## varname))) = (initval)
 
+#else
+    #error "Cannot compile Asterisk: unknown and unsupported compiler."
+#endif /* #if __GNUC__ */
+
 /*!
  * \brief Asterisk wrapper around crypt(3).
  *
@@ -1050,4 +1089,27 @@ char *ast_crypt_encrypt(const char *key);
  */
 int ast_crypt_validate(const char *key, const char *expected);
 
+/*
+ * \brief Test that a file exists and is readable by the effective user.
+ * \since 13.7.0
+ *
+ * \param filename File to test.
+ * \return True (non-zero) if the file exists and is readable.
+ * \return False (zero) if the file either doesn't exists or is not readable.
+ */
+int ast_file_is_readable(const char *filename);
+
+/*
+ * \brief Compare 2 major.minor.patch.extra version strings.
+ * \since 13.7.0
+ *
+ * \param version1.
+ * \param version2.
+ *
+ * \return <0 if version 1 < version 2.
+ * \return =0 if version 1 = version 2.
+ * \return >0 if version 1 > version 2.
+ */
+int ast_compare_versions(const char *version1, const char *version2);
+
 #endif /* _ASTERISK_UTILS_H */
diff --git a/include/asterisk/vector.h b/include/asterisk/vector.h
index 0053d8a..0a13c56 100644
--- a/include/asterisk/vector.h
+++ b/include/asterisk/vector.h
@@ -19,6 +19,8 @@
 #ifndef _ASTERISK_VECTOR_H
 #define _ASTERISK_VECTOR_H
 
+#include "asterisk/lock.h"
+
 /*! \file
  *
  * \brief Vector container support.
@@ -47,6 +49,20 @@
 	}
 
 /*!
+ * \brief Define a vector structure with a read/write lock
+ *
+ * \param name Optional vector struct name.
+ * \param type Vector element type.
+ */
+#define AST_VECTOR_RW(name, type) \
+	struct name {            \
+		type *elems;         \
+		size_t max;          \
+		size_t current;      \
+		ast_rwlock_t lock;   \
+	}
+
+/*!
  * \brief Initialize a vector
  *
  * If \a size is 0, then no space will be allocated until the vector is
@@ -72,6 +88,26 @@
 })
 
 /*!
+ * \brief Initialize a vector with a read/write lock
+ *
+ * If \a size is 0, then no space will be allocated until the vector is
+ * appended to.
+ *
+ * \param vec Vector to initialize.
+ * \param size Initial size of the vector.
+ *
+ * \return 0 on success.
+ * \return Non-zero on failure.
+ */
+#define AST_VECTOR_RW_INIT(vec, size) ({ \
+	int res = -1; \
+	if (AST_VECTOR_INIT(vec, size) == 0) { \
+		res = ast_rwlock_init(&(vec)->lock); \
+	} \
+	res; \
+})
+
+/*!
  * \brief Deallocates this vector.
  *
  * If any code to free the elements of this vector need to be run, that should
@@ -87,6 +123,70 @@
 } while (0)
 
 /*!
+ * \brief Deallocates this vector pointer.
+ *
+ * If any code to free the elements of this vector need to be run, that should
+ * be done prior to this call.
+ *
+ * \param vec Pointer to a malloc'd vector structure.
+ */
+#define AST_VECTOR_PTR_FREE(vec) do { \
+	AST_VECTOR_FREE(vec); \
+	ast_free(vec); \
+} while (0)
+
+/*!
+ * \brief Deallocates this locked vector
+ *
+ * If any code to free the elements of this vector need to be run, that should
+ * be done prior to this call.
+ *
+ * \param vec Vector to deallocate.
+ */
+#define AST_VECTOR_RW_FREE(vec) do { \
+	AST_VECTOR_FREE(vec); \
+	ast_rwlock_destroy(&(vec)->lock); \
+} while(0)
+
+/*!
+ * \brief Deallocates this locked vector pointer.
+ *
+ * If any code to free the elements of this vector need to be run, that should
+ * be done prior to this call.
+ *
+ * \param vec Pointer to a malloc'd vector structure.
+ */
+#define AST_VECTOR_RW_PTR_FREE(vec) do { \
+	AST_VECTOR_RW_FREE(vec); \
+	ast_free(vec); \
+} while(0)
+
+/*!
+ * \internal
+ */
+#define __make_room(idx, vec) ({ \
+	int res = 0;								\
+	do {														\
+		if ((idx) >= (vec)->max) {								\
+			size_t new_max = ((idx) + 1) * 2;				\
+			typeof((vec)->elems) new_elems = ast_calloc(1,		\
+				new_max * sizeof(*new_elems));					\
+			if (new_elems) {									\
+				memcpy(new_elems, (vec)->elems,					\
+					(vec)->current * sizeof(*new_elems)); 		\
+				ast_free((vec)->elems);							\
+				(vec)->elems = new_elems;						\
+				(vec)->max = new_max;							\
+			} else {											\
+				res = -1;										\
+				break;											\
+			}													\
+		}														\
+	} while(0);													\
+	res;														\
+})
+
+/*!
  * \brief Append an element to a vector, growing the vector if needed.
  *
  * \param vec Vector to append to.
@@ -96,23 +196,46 @@
  * \return Non-zero on failure.
  */
 #define AST_VECTOR_APPEND(vec, elem) ({						\
-	int res = 0;								\
-	do {									\
-		if ((vec)->current + 1 > (vec)->max) {				\
-			size_t new_max = (vec)->max ? 2 * (vec)->max : 1;	\
-			typeof((vec)->elems) new_elems = ast_realloc(		\
-				(vec)->elems, new_max * sizeof(*new_elems));	\
-			if (new_elems) {					\
-				(vec)->elems = new_elems;			\
-				(vec)->max = new_max;				\
-			} else {						\
-				res = -1;					\
-				break;						\
-			}							\
-		}								\
+	int res = 0;											\
+	do {													\
+		if (__make_room((vec)->current, vec) != 0) { 		\
+			res = -1;										\
+			break;											\
+		} 													\
 		(vec)->elems[(vec)->current++] = (elem);			\
-	} while (0);								\
-	res;									\
+	} while (0);											\
+	res;													\
+})
+
+/*!
+ * \brief Replace an element at a specific position in a vector, growing the vector if needed.
+ *
+ * \param vec Vector to replace into.
+ * \param idx Position to replace.
+ * \param elem Element to replace.
+ *
+ * \return 0 on success.
+ * \return Non-zero on failure.
+ *
+ * \warning This macro will overwrite anything already present at the position provided.
+ *
+ * \warning Use of this macro with the expectation that the element will remain at the provided
+ * index means you can not use the UNORDERED assortment of macros. These macros alter the ordering
+ * of the vector itself.
+ */
+#define AST_VECTOR_REPLACE(vec, idx, elem) ({		\
+	int res = 0;									\
+	do {											\
+		if (__make_room((idx), vec) != 0) {			\
+			res = -1;								\
+			break;									\
+		}											\
+		(vec)->elems[(idx)] = (elem);				\
+		if (((idx) + 1) > (vec)->current) {			\
+			(vec)->current = (idx) + 1;				\
+		}											\
+	} while(0);										\
+	res;											\
 })
 
 /*!
@@ -125,36 +248,58 @@
  * \return 0 on success.
  * \return Non-zero on failure.
  *
- * \warning This macro will overwrite anything already present at the position provided.
+ * \warning This macro will shift existing elements right to make room for the new element.
  *
  * \warning Use of this macro with the expectation that the element will remain at the provided
  * index means you can not use the UNORDERED assortment of macros. These macros alter the ordering
  * of the vector itself.
  */
-#define AST_VECTOR_INSERT(vec, idx, elem) ({					\
- 	int res = 0;												\
- 	do {														\
- 		if (((idx) + 1) > (vec)->max) {							\
- 			size_t new_max = ((idx) + 1) * 2;					\
-			typeof((vec)->elems) new_elems = ast_calloc(1,		\
-				new_max * sizeof(*new_elems));					\
-			if (new_elems) {									\
-				memcpy(new_elems, (vec)->elems,					\
-					(vec)->current * sizeof(*new_elems)); 		\
-				ast_free((vec)->elems);							\
-				(vec)->elems = new_elems;						\
-				(vec)->max = new_max;							\
-			} else {											\
-				res = -1;										\
-				break;											\
-			}													\
- 		}														\
- 		(vec)->elems[(idx)] = (elem);							\
- 		if (((idx) + 1) > (vec)->current) {						\
- 			(vec)->current = (idx) + 1;							\
- 		}														\
- 	} while(0);													\
- 	res;														\
+#define AST_VECTOR_INSERT_AT(vec, idx, elem) ({ \
+	int res = 0; \
+	size_t __move; \
+	do { \
+		if (__make_room(((idx) > (vec)->current ? (idx) : (vec)->current), vec) != 0) {							\
+			res = -1;										\
+			break;											\
+		}														\
+		if ((vec)->current > 0 && (idx) < (vec)->current) { \
+			__move = ((vec)->current - (idx)) * sizeof(typeof((vec)->elems[0])); \
+			memmove(&(vec)->elems[(idx) + 1], &(vec)->elems[(idx)], __move); \
+		} \
+		(vec)->elems[(idx)] = (elem); \
+		(vec)->current = ((idx) > (vec)->current ? (idx) : (vec)->current) + 1; \
+	} while (0); \
+	res; \
+})
+
+/*!
+ * \brief Add an element into a sorted vector
+ *
+ * \param vec Sorted vector to add to.
+ * \param elem Element to insert.
+ * \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
+ */
+#define AST_VECTOR_ADD_SORTED(vec, elem, cmp) ({ \
+	int res = 0; \
+	size_t __idx = (vec)->current; \
+	do { \
+		if (__make_room((vec)->current, vec) != 0) { \
+			res = -1; \
+			break; \
+		} \
+		while (__idx > 0 && (cmp((vec)->elems[__idx - 1], elem) > 0)) { \
+			(vec)->elems[__idx] = (vec)->elems[__idx - 1]; \
+			__idx--; \
+		} \
+		(vec)->elems[__idx] = elem; \
+		(vec)->current++; \
+	} while (0); \
+	res; \
 })
 
 /*!
@@ -165,36 +310,48 @@
  *
  * \param vec Vector to remove from.
  * \param idx Index of the element to remove.
+ * \param preserve_order Preserve the vector order.
+ *
  * \return The element that was removed.
  */
-#define AST_VECTOR_REMOVE_UNORDERED(vec, idx) ({		\
-	typeof((vec)->elems[0]) res;				\
-	size_t __idx = (idx);					\
-	ast_assert(__idx < (vec)->current);			\
-	res = (vec)->elems[__idx];				\
-	(vec)->elems[__idx] = (vec)->elems[--(vec)->current];	\
+#define AST_VECTOR_REMOVE(vec, idx, preserve_ordered) ({ \
+	typeof((vec)->elems[0]) res; \
+	size_t __idx = (idx); \
+	ast_assert(__idx < (vec)->current); \
+	res = (vec)->elems[__idx]; \
+	if ((preserve_ordered)) { \
+		size_t __move; \
+		__move = ((vec)->current - (__idx) - 1) * sizeof(typeof((vec)->elems[0])); \
+		memmove(&(vec)->elems[__idx], &(vec)->elems[__idx + 1], __move); \
+		(vec)->current--; \
+	} else { \
+		(vec)->elems[__idx] = (vec)->elems[--(vec)->current];	\
+	}; \
 	res;							\
 })
 
 /*!
- * \brief Remove an element from a vector by index while maintaining order.
+ * \brief Remove an element from an unordered vector by index.
+ *
+ * Note that elements in the vector may be reordered, so that the remove can
+ * happen in constant time.
  *
  * \param vec Vector to remove from.
  * \param idx Index of the element to remove.
  * \return The element that was removed.
  */
-#define AST_VECTOR_REMOVE_ORDERED(vec, idx) ({				\
-      	typeof((vec)->elems[0]) res;					\
-	size_t __idx = (idx);						\
-	size_t __move;							\
-	ast_assert(__idx < (vec)->current);				\
-	res = (vec)->elems[__idx];					\
-	__move = ((vec)->current - (__idx) - 1) * sizeof(typeof((vec)->elems[0])); \
-	memmove(&(vec)->elems[__idx], &(vec)->elems[__idx + 1], __move); \
-	(vec)->current--;						\
-	res;								\
-})
+#define AST_VECTOR_REMOVE_UNORDERED(vec, idx) \
+	AST_VECTOR_REMOVE(vec, idx, 0)
 
+/*!
+ * \brief Remove an element from a vector by index while maintaining order.
+ *
+ * \param vec Vector to remove from.
+ * \param idx Index of the element to remove.
+ * \return The element that was removed.
+ */
+#define AST_VECTOR_REMOVE_ORDERED(vec, idx) \
+	AST_VECTOR_REMOVE(vec, idx, 1)
 
 /*!
  * \brief Remove an element from a vector that matches the given comparison
@@ -307,6 +464,17 @@
 #define AST_VECTOR_SIZE(vec) (vec)->current
 
 /*!
+ * \brief Reset vector.
+ *
+ * \param vec Vector to reset.
+ * \param callback A cleanup callback or AST_VECTOR_ELEM_CLEANUP_NOOP.
+ */
+#define AST_VECTOR_RESET(vec, cleanup) ({ \
+	AST_VECTOR_CALLBACK_VOID(vec, cleanup); \
+	(vec)->current = 0; \
+})
+
+/*!
  * \brief Get an address of element in a vector.
  *
  * \param vec Vector to query.
@@ -330,4 +498,222 @@
 	(vec)->elems[__idx];			\
 })
 
+/*!
+ * \brief Get an element from a vector that matches the given comparison
+ *
+ * \param vec Vector to get from.
+ * \param value Value to pass into comparator.
+ * \param cmp Comparator function/macros (called as \c cmp(elem, value))
+ *
+ * \return a pointer to the element that was found or NULL
+ */
+#define AST_VECTOR_GET_CMP(vec, value, cmp) ({ \
+	void *res = NULL; \
+	size_t idx; \
+	typeof(value) __value = (value); \
+	for (idx = 0; idx < (vec)->current; ++idx) { \
+		if (cmp((vec)->elems[idx], __value)) { \
+			res = &(vec)->elems[idx]; \
+			break; \
+		} \
+	} \
+	res; \
+})
+
+/*!
+ * \brief Default callback for AST_VECTOR_CALLBACK()
+ *
+ * \param elem Element to compare against
+ * \param value Value to compare with the vector element.
+ *
+ * \return CMP_MATCH always.
+ */
+#define AST_VECTOR_MATCH_ALL(element) (CMP_MATCH)
+
+
+/*!
+ * \brief Execute a callback on every element in a vector returning the first matched
+ *
+ * \param vec Vector to operate on.
+ * \param callback A callback that takes at least 1 argument (the element)
+ * plus number of optional arguments
+ * \param default_value A default value to return if no elements matched
+ *
+ * \return the first element matched before CMP_STOP was returned
+ * or the end of the vector was reached. Otherwise, default_value
+ */
+#define AST_VECTOR_CALLBACK(vec, callback, default_value, ...) ({ \
+	size_t idx; \
+	typeof((vec)->elems[0]) res = default_value;				\
+	for (idx = 0; idx < (vec)->current; idx++) { \
+		int rc = callback((vec)->elems[idx], ##__VA_ARGS__);	\
+		if (rc & CMP_MATCH) { \
+			res = (vec)->elems[idx]; \
+			break; \
+		}\
+		if (rc & CMP_STOP) { \
+			break; \
+		}\
+	} \
+	res; \
+})
+
+/*!
+ * \brief Execute a callback on every element in a vector returning the matching
+ * elements in a new vector
+ *
+ * This macro basically provides a filtered clone.
+ *
+ * \param vec Vector to operate on.
+ * \param callback A callback that takes at least 1 argument (the element)
+ * plus number of optional arguments
+ *
+ * \return a vector containing the elements matched before CMP_STOP was returned
+ * or the end of the vector was reached. The vector may be empty and could be NULL
+ * if there was not enough memory to allocate it's control structure.
+ *
+ * \warning The returned vector must have AST_VECTOR_PTR_FREE()
+ * called on it after you've finished with it.
+ *
+ * \note The type of the returned vector must be traceable to the original vector.
+ *
+ * The following will resut in "error: assignment from incompatible pointer type"
+ * because these declare 2 different structures.
+ *
+ * \code
+ * AST_VECTOR(, char *) vector_1;
+ * AST_VECTOR(, char *) *vector_2;
+ *
+ * vector_2 = AST_VECTOR_CALLBACK_MULTIPLE(&vector_1, callback);
+ * \endcode
+ *
+ * This will work because you're using the type of the first
+ * to declare the second:
+ *
+ * \code
+ * AST_VECTOR(mytype, char *) vector_1;
+ * struct mytype *vector_2 = NULL;
+ *
+ * vector_2 = AST_VECTOR_CALLBACK_MULTIPLE(&vector_1, callback);
+ * \endcode
+ *
+ * This will also work because you're declaring both vector_1 and
+ * vector_2 from the same definition.
+ *
+ * \code
+ * AST_VECTOR(, char *) vector_1, *vector_2 = NULL;
+ *
+ * vector_2 = AST_VECTOR_CALLBACK_MULTIPLE(&vector_1, callback);
+ * \endcode
+ */
+#define AST_VECTOR_CALLBACK_MULTIPLE(vec, callback, ...) ({ \
+	size_t idx; \
+	typeof((vec)) new_vec; \
+	do { \
+		new_vec = ast_malloc(sizeof(*new_vec)); \
+		if (!new_vec) { \
+			break; \
+		} \
+		if (AST_VECTOR_INIT(new_vec, AST_VECTOR_SIZE((vec))) != 0) { \
+			ast_free(new_vec); \
+			new_vec = NULL; \
+			break; \
+		} \
+		for (idx = 0; idx < (vec)->current; idx++) { \
+			int rc = callback((vec)->elems[idx], ##__VA_ARGS__);	\
+			if (rc & CMP_MATCH) { \
+				AST_VECTOR_APPEND(new_vec, (vec)->elems[idx]); \
+			} \
+			if (rc & CMP_STOP) { \
+				break; \
+			}\
+		} \
+	} while(0); \
+	new_vec; \
+})
+
+/*!
+ * \brief Execute a callback on every element in a vector disregarding callback return
+ *
+ * \param vec Vector to operate on.
+ * \param callback A callback that takes at least 1 argument (the element)
+ * plus number of optional arguments
+ */
+#define AST_VECTOR_CALLBACK_VOID(vec, callback, ...) ({ \
+	size_t idx; \
+	for (idx = 0; idx < (vec)->current; idx++) { \
+		callback((vec)->elems[idx], ##__VA_ARGS__);	\
+	} \
+})
+
+/*!
+ * \brief Obtain read lock on vector
+ *
+ * \param vec Vector to operate on.
+ *
+ * \return 0 if success
+ * \return Non-zero if error
+ */
+#define AST_VECTOR_RW_RDLOCK(vec) ast_rwlock_rdlock(&(vec)->lock)
+
+/*!
+ * \brief Obtain write lock on vector
+ *
+ * \param vec Vector to operate on.
+ *
+ * \return 0 if success
+ * \return Non-zero if error
+ */
+#define AST_VECTOR_RW_WRLOCK(vec) ast_rwlock_wrlock(&(vec)->lock)
+
+/*!
+ * \brief Unlock vector
+ *
+ * \param vec Vector to operate on.
+ *
+ * \return 0 if success
+ * \return Non-zero if error
+ */
+#define AST_VECTOR_RW_UNLOCK(vec) ast_rwlock_unlock(&(vec)->lock)
+
+/*!
+ * \brief Try to obtain read lock on vector failing immediately if unable
+ *
+ * \param vec Vector to operate on.
+ *
+ * \return 0 if success
+ * \return Non-zero if error
+ */
+#define AST_VECTOR_RW_RDLOCK_TRY(vec) ast_rwlock_tryrdlock(&(vec)->lock)
+
+/*!
+ * \brief Try to obtain write lock on vector failing immediately if unable
+ *
+ * \param vec Vector to operate on.
+ *
+ * \return 0 if success
+ * \return Non-zero if error
+ */
+#define AST_VECTOR_RW_WRLOCK_TRY(vec) ast_rwlock_trywrlock(&(vec)->lock)
+
+/*!
+ * \brief Try to obtain read lock on vector failing after timeout if unable
+ *
+ * \param vec Vector to operate on.
+ *
+ * \return 0 if success
+ * \return Non-zero if error
+ */
+#define AST_VECTOR_RW_RDLOCK_TIMED(vec, timespec) ast_rwlock_timedrdlock(&(vec)->lock, timespec)
+
+/*!
+ * \brief Try to obtain write lock on vector failing after timeout if unable
+ *
+ * \param vec Vector to operate on.
+ *
+ * \return 0 if success
+ * \return Non-zero if error
+ */
+#define AST_VECTOR_RW_WRLOCK_TIMED(vec, timespec) ast_rwlock_timedwrlock(&(vec)->lock, timespec)
+
 #endif /* _ASTERISK_VECTOR_H */
diff --git a/main/Makefile b/main/Makefile
index 0fa2192..2c4b576 100644
--- a/main/Makefile
+++ b/main/Makefile
@@ -40,6 +40,7 @@ AST_LIBS+=$(JANSSON_LIB)
 AST_LIBS+=$(URIPARSER_LIB)
 AST_LIBS+=$(UUID_LIB)
 AST_LIBS+=$(CRYPT_LIB)
+AST_LIBS+=$(AST_CLANG_BLOCKS_LIBS)
 
 ifneq ($(findstring $(OSARCH), linux-gnu uclinux linux-uclibc kfreebsd-gnu),)
   ifneq ($(findstring LOADABLE_MODULES,$(MENUSELECT_CFLAGS)),)
@@ -59,7 +60,7 @@ endif
 
 ifneq ($(findstring darwin,$(OSARCH)),)
   AST_LIBS+=-lresolv
-  ASTLINK=-mmacosx-version-min=10.6 -Xlinker -undefined -Xlinker dynamic_lookup -force_flat_namespace
+  ASTLINK=-mmacosx-version-min=10.6 -Wl,-undefined,dynamic_lookup -force_flat_namespace
   ASTLINK+=/usr/lib/bundle1.o
 else
 # These are used for all but Darwin
@@ -185,7 +186,7 @@ ifneq ($(findstring ENABLE_UPLOADS,$(MENUSELECT_CFLAGS)),)
 GMIMELDFLAGS+=$(GMIME_LIB)
 endif
 
-$(OBJS): _ASTCFLAGS+=-DAST_MODULE=\"core\"
+$(OBJS): _ASTCFLAGS+=-DAST_MODULE=\"core\" -DAST_IN_CORE
 
 libasteriskssl.o: _ASTCFLAGS+=$(OPENSSL_INCLUDE)
 
diff --git a/main/abstract_jb.c b/main/abstract_jb.c
index 6413465..b629fe8 100644
--- a/main/abstract_jb.c
+++ b/main/abstract_jb.c
@@ -35,7 +35,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 427021 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/frame.h"
 #include "asterisk/channel.h"
diff --git a/main/acl.c b/main/acl.c
index c4c1d7a..87776b3 100644
--- a/main/acl.c
+++ b/main/acl.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428425 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/network.h"
 
diff --git a/main/alaw.c b/main/alaw.c
index 51ffe72..c5069f3 100644
--- a/main/alaw.c
+++ b/main/alaw.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 369013 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/alaw.h"
 #include "asterisk/logger.h"
diff --git a/main/aoc.c b/main/aoc.c
index 413c741..29c5e87 100644
--- a/main/aoc.c
+++ b/main/aoc.c
@@ -28,7 +28,7 @@
  ***/
 
 #include "asterisk.h"
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 416216 $");
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
 
 #include "asterisk/aoc.h"
 #include "asterisk/utils.h"
@@ -1667,11 +1667,11 @@ static struct ast_json *charge_to_json(const struct ast_aoc_decoded *decoded)
 	}
 
 	return ast_json_pack(
-		"{s:s, s:s, s:s, s:O}",
+		"{s:s, s:s, s:s, s:o}",
 		"Type", aoc_charge_type_str(decoded->charge_type),
 		"BillingID", aoc_billingid_str(decoded->billing_id),
 		"TotalType", aoc_type_of_totaling_str(decoded->total_type),
-		obj_type, obj);
+		obj_type, ast_json_ref(obj));
 }
 
 static struct ast_json *association_to_json(const struct ast_aoc_decoded *decoded)
@@ -1738,10 +1738,10 @@ static struct ast_json *s_to_json(const struct ast_aoc_decoded *decoded)
 					"Scale", decoded->aoc_s_entries[i].rate.duration.granularity_time_scale);
 			}
 
-			type = ast_json_pack("{s:O, s:s, s:O, s:O}", "Currency", currency, "ChargingType",
+			type = ast_json_pack("{s:o, s:s, s:o, s:o}", "Currency", ast_json_ref(currency), "ChargingType",
 					     decoded->aoc_s_entries[i].rate.duration.charging_type ?
-					     "StepFunction" : "ContinuousCharging", "Time", time,
-					     "Granularity", granularity ? granularity : ast_json_null());
+					     "StepFunction" : "ContinuousCharging", "Time", ast_json_ref(time),
+					     "Granularity", granularity ? ast_json_ref(granularity) : ast_json_ref(ast_json_null()));
 
 			break;
 		}
@@ -1751,7 +1751,7 @@ static struct ast_json *s_to_json(const struct ast_aoc_decoded *decoded)
 				decoded->aoc_s_entries[i].rate.flat.amount,
 				decoded->aoc_s_entries[i].rate.flat.multiplier);
 
-			type = ast_json_pack("{s:O}", "Currency", currency);
+			type = ast_json_pack("{s:o}", "Currency", ast_json_ref(currency));
 			break;
 		case AST_AOC_RATE_TYPE_VOLUME:
 			currency = currency_to_json(
@@ -1760,9 +1760,9 @@ static struct ast_json *s_to_json(const struct ast_aoc_decoded *decoded)
 				decoded->aoc_s_entries[i].rate.volume.multiplier);
 
 			type = ast_json_pack(
-				"{s:s, s:O}", "Unit", aoc_volume_unit_str(
+				"{s:s, s:o}", "Unit", aoc_volume_unit_str(
 					decoded->aoc_s_entries[i].rate.volume.volume_unit),
-				"Currency", currency);
+				"Currency", ast_json_ref(currency));
 			break;
 		case AST_AOC_RATE_TYPE_SPECIAL_CODE:
 			type = ast_json_pack("{s:i}", "SpecialCode",
@@ -1772,8 +1772,8 @@ static struct ast_json *s_to_json(const struct ast_aoc_decoded *decoded)
 			break;
 		}
 
-		rate = ast_json_pack("{s:s, s:O}", "Chargeable", charge_item,
-				     aoc_rate_type_str(decoded->aoc_s_entries[i].rate_type), type);
+		rate = ast_json_pack("{s:s, s:o}", "Chargeable", charge_item,
+				     aoc_rate_type_str(decoded->aoc_s_entries[i].rate_type), ast_json_ref(type));
 		if (ast_json_array_append(rates, rate)) {
 			break;
 		}
@@ -1793,24 +1793,87 @@ static struct ast_json *e_to_json(const struct ast_aoc_decoded *decoded)
 			     "Charge", charge_to_json(decoded));
 }
 
+struct aoc_event_blob {
+	/*! Channel AOC event is associated with (NULL for unassociated) */
+	struct ast_channel_snapshot *snapshot;
+	/*! AOC JSON blob of data */
+	struct ast_json *blob;
+};
+
+static void aoc_event_blob_dtor(void *obj)
+{
+	struct aoc_event_blob *aoc_event = obj;
+
+	ao2_cleanup(aoc_event->snapshot);
+	ast_json_unref(aoc_event->blob);
+}
+
+/*!
+ * \internal
+ * \brief Publish an AOC event.
+ * \since 13.3.0
+ *
+ * \param chan Channel associated with the AOC event. (May be NULL if no channel)
+ * \param msg_type What kind of AOC event.
+ * \param blob AOC data blob to publish.
+ *
+ * \return Nothing
+ */
+static void aoc_publish_blob(struct ast_channel *chan, struct stasis_message_type *msg_type, struct ast_json *blob)
+{
+	struct stasis_message *msg;
+	struct aoc_event_blob *aoc_event;
+
+	if (!blob || ast_json_is_null(blob)) {
+		/* No AOC blob information?  Nothing to send an event about. */
+		return;
+	}
+
+	aoc_event = ao2_alloc_options(sizeof(*aoc_event), aoc_event_blob_dtor,
+		AO2_ALLOC_OPT_LOCK_NOLOCK);
+	if (!aoc_event) {
+		return;
+	}
+
+	if (chan) {
+		aoc_event->snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(chan));
+		if (!aoc_event->snapshot) {
+			ao2_ref(aoc_event, -1);
+			return;
+		}
+	}
+	aoc_event->blob = ast_json_ref(blob);
+
+	msg = stasis_message_create(msg_type, aoc_event);
+	ao2_ref(aoc_event, -1);
+
+	stasis_publish(ast_manager_get_topic(), msg);
+}
+
 static struct ast_manager_event_blob *aoc_to_ami(struct stasis_message *message,
 						 const char *event_name)
 {
-	struct ast_channel_blob *obj = stasis_message_data(message);
-	RAII_VAR(struct ast_str *, channel, NULL, ast_free);
-	RAII_VAR(struct ast_str *, aoc, NULL, ast_free);
+	struct aoc_event_blob *aoc_event = stasis_message_data(message);
+	struct ast_str *channel = NULL;
+	struct ast_str *aoc;
+	struct ast_manager_event_blob *ev = NULL;
 
-	if (!(channel = ast_manager_build_channel_state_string(
-		      obj->snapshot))) {
-		return NULL;
+	if (aoc_event->snapshot) {
+		channel = ast_manager_build_channel_state_string(aoc_event->snapshot);
+		if (!channel) {
+			return NULL;
+		}
 	}
 
-	if (!(aoc = ast_manager_str_from_json_object(obj->blob, NULL))) {
-		return NULL;
+	aoc = ast_manager_str_from_json_object(aoc_event->blob, NULL);
+	if (aoc && !ast_strlen_zero(ast_str_buffer(aoc))) {
+		ev = ast_manager_event_blob_create(EVENT_FLAG_AOC, event_name, "%s%s",
+			AS_OR(channel, ""), ast_str_buffer(aoc));
 	}
 
-	return ast_manager_event_blob_create(EVENT_FLAG_AOC, event_name, "%s%s",
-					     AS_OR(channel, ""), ast_str_buffer(aoc));
+	ast_free(aoc);
+	ast_free(channel);
+	return ev;
 }
 
 static struct ast_manager_event_blob *aoc_s_to_ami(struct stasis_message *message)
@@ -1846,7 +1909,7 @@ STASIS_MESSAGE_TYPE_DEFN(
 
 int ast_aoc_manager_event(const struct ast_aoc_decoded *decoded, struct ast_channel *chan)
 {
-	RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
+	struct ast_json *blob;
 	struct stasis_message_type *msg_type;
 
 	if (!decoded) {
@@ -1871,7 +1934,8 @@ int ast_aoc_manager_event(const struct ast_aoc_decoded *decoded, struct ast_chan
 		return 0;
 	}
 
-	ast_channel_publish_cached_blob(chan, msg_type, blob);
+	aoc_publish_blob(chan, msg_type, blob);
+	ast_json_unref(blob);
 	return 0;
 }
 
@@ -1947,6 +2011,6 @@ int ast_aoc_cli_init(void)
 	STASIS_MESSAGE_TYPE_INIT(aoc_d_type);
 	STASIS_MESSAGE_TYPE_INIT(aoc_e_type);
 
-	ast_register_atexit(aoc_shutdown);
+	ast_register_cleanup(aoc_shutdown);
 	return ast_cli_register_multiple(aoc_cli, ARRAY_LEN(aoc_cli));
 }
diff --git a/main/app.c b/main/app.c
index c69eeea..8c33b91 100644
--- a/main/app.c
+++ b/main/app.c
@@ -35,7 +35,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 427181 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
@@ -519,6 +519,57 @@ void ast_vm_unregister(const char *module_name)
 	ao2_cleanup(table);
 }
 
+#ifdef TEST_FRAMEWORK
+/*! \brief Holding container for the voicemail provider used while testing */
+static AO2_GLOBAL_OBJ_STATIC(vm_provider_holder);
+static int provider_is_swapped = 0;
+
+void ast_vm_test_swap_table_in(const struct ast_vm_functions *vm_table)
+{
+	RAII_VAR(struct ast_vm_functions *, holding_table, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_vm_functions *, new_table, NULL, ao2_cleanup);
+
+	if (provider_is_swapped) {
+		ast_log(LOG_ERROR, "Attempted to swap in test function table without swapping out old test table.\n");
+		return;
+	}
+
+	holding_table = ao2_global_obj_ref(vm_provider);
+
+	if (holding_table) {
+		ao2_global_obj_replace_unref(vm_provider_holder, holding_table);
+	}
+
+	new_table = ao2_alloc_options(sizeof(*new_table), NULL, AO2_ALLOC_OPT_LOCK_NOLOCK);
+	if (!new_table) {
+		return;
+	}
+	*new_table = *vm_table;
+
+	ao2_global_obj_replace_unref(vm_provider, new_table);
+	provider_is_swapped = 1;
+}
+
+void ast_vm_test_swap_table_out(void)
+{
+	RAII_VAR(struct ast_vm_functions *, held_table, NULL, ao2_cleanup);
+
+	if (!provider_is_swapped) {
+		ast_log(LOG_ERROR, "Attempted to swap out test function table, but none is currently installed.\n");
+		return;
+	}
+
+	held_table = ao2_global_obj_ref(vm_provider_holder);
+	if (!held_table) {
+		return;
+	}
+
+	ao2_global_obj_replace_unref(vm_provider, held_table);
+	ao2_global_obj_release(vm_provider_holder);
+	provider_is_swapped = 0;
+}
+#endif
+
 /*! \brief The container for the voicemail greeter provider */
 static AO2_GLOBAL_OBJ_STATIC(vm_greeter_provider);
 
@@ -1464,6 +1515,9 @@ static int __ast_play_and_record(struct ast_channel *chan, const char *playfile,
 	case AST_RECORD_IF_EXISTS_APPEND:
 		ioflags |= O_APPEND;
 		break;
+	case AST_RECORD_IF_EXISTS_ERROR:
+		ast_assert(0);
+		break;
 	}
 
 	if (silencethreshold < 0) {
@@ -2911,7 +2965,9 @@ int ast_safe_fork(int stop_reaper)
 		ast_replace_sigchld();
 	}
 
-	sigfillset(&signal_set);
+	/* GCC 4.9 gives a bogus "right-hand operand of comma expression has
+	 * no effect" warning */
+	(void) sigfillset(&signal_set);
 	pthread_sigmask(SIG_BLOCK, &signal_set, &old_set);
 
 	pid = fork();
diff --git a/main/ast_expr2.c b/main/ast_expr2.c
index 5c9e01d..798e3d3 100644
--- a/main/ast_expr2.c
+++ b/main/ast_expr2.c
@@ -98,7 +98,7 @@
 
 #if !defined(STANDALONE) && !defined(STANDALONE2)	\
 	
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 369940 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #else
 #ifndef __USE_ISOC99
 #define __USE_ISOC99 1
diff --git a/main/ast_expr2.fl b/main/ast_expr2.fl
index 8bc33fd..e10dde2 100644
--- a/main/ast_expr2.fl
+++ b/main/ast_expr2.fl
@@ -26,7 +26,7 @@
 #include <stdio.h>
 
 #if !defined(STANDALONE)
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 362307 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #else
 #ifndef __USE_ISOC99
 #define __USE_ISOC99 1
diff --git a/main/ast_expr2.y b/main/ast_expr2.y
index b3c2585..83d3eff 100644
--- a/main/ast_expr2.y
+++ b/main/ast_expr2.y
@@ -19,7 +19,7 @@
 
 #if !defined(STANDALONE) && !defined(STANDALONE2)	\
 	
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 360359 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #else
 #ifndef __USE_ISOC99
 #define __USE_ISOC99 1
diff --git a/main/ast_expr2f.c b/main/ast_expr2f.c
index 68860fd..1f67d46 100644
--- a/main/ast_expr2f.c
+++ b/main/ast_expr2f.c
@@ -520,7 +520,7 @@ static yyconst flex_int16_t yy_chk[159] =
 #include <stdio.h>
 
 #if !defined(STANDALONE)
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 373330 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #else
 #ifndef __USE_ISOC99
 #define __USE_ISOC99 1
diff --git a/main/asterisk.c b/main/asterisk.c
index 5e127b3..3f16caf 100644
--- a/main/asterisk.c
+++ b/main/asterisk.c
@@ -149,7 +149,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419044 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/_private.h"
 
@@ -171,12 +171,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419044 $")
 #elif defined(HAVE_SYSCTL)
 #include <sys/param.h>
 #include <sys/sysctl.h>
-#if !defined(__OpenBSD__)
 #include <sys/vmmeter.h>
 #if defined(__FreeBSD__)
 #include <vm/vm_param.h>
 #endif
-#endif
 #if defined(HAVE_SWAPCTL)
 #include <sys/swap.h>
 #endif
@@ -374,9 +372,14 @@ struct console consoles[AST_MAX_CONNECTS];
 
 char ast_defaultlanguage[MAX_LANGUAGE] = DEFAULT_LANGUAGE;
 
-static int ast_el_add_history(char *);
-static int ast_el_read_history(char *);
-static int ast_el_write_history(char *);
+static int ast_el_add_history(const char *);
+static int ast_el_read_history(const char *);
+static int ast_el_write_history(const char *);
+
+static void ast_el_read_default_histfile(void);
+static void ast_el_write_default_histfile(void);
+
+static void asterisk_daemon(int isroot, const char *runuser, const char *rungroup);
 
 struct _cfg_paths {
 	char config_dir[PATH_MAX];
@@ -432,16 +435,34 @@ static char ast_config_AST_CTL[PATH_MAX] = "asterisk.ctl";
 extern unsigned int ast_FD_SETSIZE;
 
 static char *_argv[256];
+
 typedef enum {
-	NOT_SHUTTING_DOWN = -2,
-	SHUTTING_DOWN = -1,
-	/* Valid values for quit_handler niceness below: */
+	/*! Normal operation */
+	NOT_SHUTTING_DOWN,
+	/*! Committed to shutting down.  Final phase */
+	SHUTTING_DOWN_FINAL,
+	/*! Committed to shutting down.  Initial phase */
+	SHUTTING_DOWN,
+	/*!
+	 * Valid values for quit_handler() niceness below.
+	 * These shutdown/restart levels can be cancelled.
+	 *
+	 * Remote console exit right now
+	 */
 	SHUTDOWN_FAST,
+	/*! core stop/restart now */
 	SHUTDOWN_NORMAL,
+	/*! core stop/restart gracefully */
 	SHUTDOWN_NICE,
+	/*! core stop/restart when convenient */
 	SHUTDOWN_REALLY_NICE
 } shutdown_nice_t;
+
 static shutdown_nice_t shuttingdown = NOT_SHUTTING_DOWN;
+
+/*! Prevent new channel allocation for shutdown. */
+static int shutdown_pending;
+
 static int restartnow;
 static pthread_t consolethread = AST_PTHREADT_NULL;
 static pthread_t mon_sig_flags;
@@ -459,84 +480,79 @@ static struct {
 } sig_flags;
 
 #if !defined(LOW_MEMORY)
-struct file_version {
-	AST_RWLIST_ENTRY(file_version) list;
+struct registered_file {
+	AST_RWLIST_ENTRY(registered_file) list;
 	const char *file;
-	char *version;
 };
 
-static AST_RWLIST_HEAD_STATIC(file_versions, file_version);
+static AST_RWLIST_HEAD_STATIC(registered_files, registered_file);
 
 void ast_register_file_version(const char *file, const char *version)
 {
-	struct file_version *new;
-	char *work;
-	size_t version_length;
-
-	work = ast_strdupa(version);
-	work = ast_strip(ast_strip_quoted(work, "$", "$"));
-	version_length = strlen(work) + 1;
+	struct registered_file *reg;
 
-	if (!(new = ast_calloc(1, sizeof(*new) + version_length)))
+	reg = ast_calloc(1, sizeof(*reg));
+	if (!reg) {
 		return;
+	}
 
-	new->file = file;
-	new->version = (char *) new + sizeof(*new);
-	memcpy(new->version, work, version_length);
-	AST_RWLIST_WRLOCK(&file_versions);
-	AST_RWLIST_INSERT_HEAD(&file_versions, new, list);
-	AST_RWLIST_UNLOCK(&file_versions);
+	reg->file = file;
+	AST_RWLIST_WRLOCK(&registered_files);
+	AST_RWLIST_INSERT_HEAD(&registered_files, reg, list);
+	AST_RWLIST_UNLOCK(&registered_files);
 }
 
 void ast_unregister_file_version(const char *file)
 {
-	struct file_version *find;
+	struct registered_file *find;
 
-	AST_RWLIST_WRLOCK(&file_versions);
-	AST_RWLIST_TRAVERSE_SAFE_BEGIN(&file_versions, find, list) {
+	AST_RWLIST_WRLOCK(&registered_files);
+	AST_RWLIST_TRAVERSE_SAFE_BEGIN(&registered_files, find, list) {
 		if (!strcasecmp(find->file, file)) {
 			AST_RWLIST_REMOVE_CURRENT(list);
 			break;
 		}
 	}
 	AST_RWLIST_TRAVERSE_SAFE_END;
-	AST_RWLIST_UNLOCK(&file_versions);
+	AST_RWLIST_UNLOCK(&registered_files);
 
-	if (find)
+	if (find) {
 		ast_free(find);
+	}
 }
 
 char *ast_complete_source_filename(const char *partial, int n)
 {
-	struct file_version *find;
+	struct registered_file *find;
 	size_t len = strlen(partial);
 	int count = 0;
 	char *res = NULL;
 
-	AST_RWLIST_RDLOCK(&file_versions);
-	AST_RWLIST_TRAVERSE(&file_versions, find, list) {
+	AST_RWLIST_RDLOCK(&registered_files);
+	AST_RWLIST_TRAVERSE(&registered_files, find, list) {
 		if (!strncasecmp(find->file, partial, len) && ++count > n) {
 			res = ast_strdup(find->file);
 			break;
 		}
 	}
-	AST_RWLIST_UNLOCK(&file_versions);
+	AST_RWLIST_UNLOCK(&registered_files);
 	return res;
 }
 
-/*! \brief Find version for given module name */
 const char *ast_file_version_find(const char *file)
 {
-	struct file_version *iterator;
+	struct registered_file *iterator;
 
-	AST_RWLIST_WRLOCK(&file_versions);
-	AST_RWLIST_TRAVERSE(&file_versions, iterator, list) {
-		if (!strcasecmp(iterator->file, file))
+	AST_RWLIST_RDLOCK(&registered_files);
+	AST_RWLIST_TRAVERSE(&registered_files, iterator, list) {
+		if (!strcasecmp(iterator->file, file)) {
 			break;
+		}
+	}
+	AST_RWLIST_UNLOCK(&registered_files);
+	if (iterator) {
+		return ast_get_version();
 	}
-	AST_RWLIST_UNLOCK(&file_versions);
-	if (iterator)
-		return iterator->version;
 	return NULL;
 }
 
@@ -606,7 +622,7 @@ static char *handle_show_settings(struct ast_cli_entry *e, int cmd, struct ast_c
 	ast_cli(a->fd, "\nPBX Core settings\n");
 	ast_cli(a->fd, "-----------------\n");
 	ast_cli(a->fd, "  Version:                     %s\n", ast_get_version());
-	ast_cli(a->fd, "  Build Options:               %s\n", S_OR(AST_BUILDOPTS, "(none)"));
+	ast_cli(a->fd, "  Build Options:               %s\n", S_OR(ast_get_build_opts(), "(none)"));
 	if (ast_option_maxcalls)
 		ast_cli(a->fd, "  Maximum calls:               %d (Current %d)\n", ast_option_maxcalls, ast_active_channels());
 	else
@@ -1019,35 +1035,35 @@ static char *handle_clear_profile(struct ast_cli_entry *e, int cmd, struct ast_c
 static char *handle_show_version_files(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
 #define FORMAT "%-25.25s %-40.40s\n"
-	struct file_version *iterator;
+	struct registered_file *iterator;
 	regex_t regexbuf;
 	int havepattern = 0;
 	int havename = 0;
 	int count_files = 0;
 	char *ret = NULL;
 	int matchlen, which = 0;
-	struct file_version *find;
+	struct registered_file *find;
 
 	switch (cmd) {
 	case CLI_INIT:
 		e->command = "core show file version [like]";
 		e->usage =
 			"Usage: core show file version [like <pattern>]\n"
-			"       Lists the revision numbers of the files used to build this copy of Asterisk.\n"
+			"       Lists the files along with the Asterisk version.\n"
 			"       Optional regular expression pattern is used to filter the file list.\n";
 		return NULL;
 	case CLI_GENERATE:
 		matchlen = strlen(a->word);
 		if (a->pos != 3)
 			return NULL;
-		AST_RWLIST_RDLOCK(&file_versions);
-		AST_RWLIST_TRAVERSE(&file_versions, find, list) {
+		AST_RWLIST_RDLOCK(&registered_files);
+		AST_RWLIST_TRAVERSE(&registered_files, find, list) {
 			if (!strncasecmp(a->word, find->file, matchlen) && ++which > a->n) {
 				ret = ast_strdup(find->file);
 				break;
 			}
 		}
-		AST_RWLIST_UNLOCK(&file_versions);
+		AST_RWLIST_UNLOCK(&registered_files);
 		return ret;
 	}
 
@@ -1072,20 +1088,20 @@ static char *handle_show_version_files(struct ast_cli_entry *e, int cmd, struct
 
 	ast_cli(a->fd, FORMAT, "File", "Revision");
 	ast_cli(a->fd, FORMAT, "----", "--------");
-	AST_RWLIST_RDLOCK(&file_versions);
-	AST_RWLIST_TRAVERSE(&file_versions, iterator, list) {
+	AST_RWLIST_RDLOCK(&registered_files);
+	AST_RWLIST_TRAVERSE(&registered_files, iterator, list) {
 		if (havename && strcasecmp(iterator->file, a->argv[4]))
 			continue;
 
 		if (havepattern && regexec(&regexbuf, iterator->file, 0, NULL, 0))
 			continue;
 
-		ast_cli(a->fd, FORMAT, iterator->file, iterator->version);
+		ast_cli(a->fd, FORMAT, iterator->file, ast_get_version());
 		count_files++;
 		if (havename)
 			break;
 	}
-	AST_RWLIST_UNLOCK(&file_versions);
+	AST_RWLIST_UNLOCK(&registered_files);
 	if (!havename) {
 		ast_cli(a->fd, "%d files listed.\n", count_files);
 	}
@@ -1867,6 +1883,43 @@ int ast_set_priority(int pri)
 	return 0;
 }
 
+int ast_shutdown_final(void)
+{
+	return shuttingdown == SHUTTING_DOWN_FINAL;
+}
+
+int ast_shutting_down(void)
+{
+	return shutdown_pending;
+}
+
+int ast_cancel_shutdown(void)
+{
+	int shutdown_aborted = 0;
+
+	ast_mutex_lock(&safe_system_lock);
+	if (shuttingdown >= SHUTDOWN_FAST) {
+		shuttingdown = NOT_SHUTTING_DOWN;
+		shutdown_pending = 0;
+		shutdown_aborted = 1;
+	}
+	ast_mutex_unlock(&safe_system_lock);
+	return shutdown_aborted;
+}
+
+/*!
+ * \internal
+ * \brief Initiate system shutdown -- prevents new channels from being allocated.
+ */
+static void ast_begin_shutdown(void)
+{
+	ast_mutex_lock(&safe_system_lock);
+	if (shuttingdown != NOT_SHUTTING_DOWN) {
+		shutdown_pending = 1;
+	}
+	ast_mutex_unlock(&safe_system_lock);
+}
+
 static int can_safely_quit(shutdown_nice_t niceness, int restart);
 static void really_quit(int num, shutdown_nice_t niceness, int restart);
 
@@ -1879,8 +1932,53 @@ static void quit_handler(int num, shutdown_nice_t niceness, int restart)
 	/* It wasn't our time. */
 }
 
+#define SHUTDOWN_TIMEOUT	15	/* Seconds */
+
+/*!
+ * \internal
+ * \brief Wait for all channels to die, a timeout, or shutdown cancelled.
+ * \since 13.3.0
+ *
+ * \param niceness Shutdown niceness in effect
+ * \param seconds Number of seconds to wait or less than zero if indefinitely.
+ *
+ * \retval zero if waiting wasn't necessary.  We were idle.
+ * \retval non-zero if we had to wait.
+ */
+static int wait_for_channels_to_die(shutdown_nice_t niceness, int seconds)
+{
+	time_t start;
+	time_t now;
+	int waited = 0;
+
+	time(&start);
+	for (;;) {
+		if (!ast_undestroyed_channels() || shuttingdown != niceness) {
+			break;
+		}
+		if (seconds < 0) {
+			/* No timeout so just poll every second */
+			sleep(1);
+		} else {
+			time(&now);
+
+			/* Wait up to the given seconds for all channels to go away */
+			if (seconds < (now - start)) {
+				break;
+			}
+
+			/* Sleep 1/10 of a second */
+			usleep(100000);
+		}
+		waited = 1;
+	}
+	return waited;
+}
+
 static int can_safely_quit(shutdown_nice_t niceness, int restart)
 {
+	int waited = 0;
+
 	/* Check if someone else isn't already doing this. */
 	ast_mutex_lock(&safe_system_lock);
 	if (shuttingdown != NOT_SHUTTING_DOWN && niceness >= shuttingdown) {
@@ -1897,40 +1995,30 @@ static int can_safely_quit(shutdown_nice_t niceness, int restart)
 	 * the atexit handlers, otherwise this would be a bit early. */
 	ast_cdr_engine_term();
 
-	/* Shutdown the message queue for the technology agnostic message channel.
-	 * This has to occur before we pause shutdown pending ast_undestroyed_channels. */
+	/*
+	 * Shutdown the message queue for the technology agnostic message channel.
+	 * This has to occur before we pause shutdown pending ast_undestroyed_channels.
+	 *
+	 * XXX This is not reversed on shutdown cancel.
+	 */
 	ast_msg_shutdown();
 
 	if (niceness == SHUTDOWN_NORMAL) {
-		time_t s, e;
 		/* Begin shutdown routine, hanging up active channels */
-		ast_begin_shutdown(1);
+		ast_begin_shutdown();
 		if (ast_opt_console) {
 			ast_verb(0, "Beginning asterisk %s....\n", restart ? "restart" : "shutdown");
 		}
-		time(&s);
-		for (;;) {
-			time(&e);
-			/* Wait up to 15 seconds for all channels to go away */
-			if ((e - s) > 15 || !ast_undestroyed_channels() || shuttingdown != niceness) {
-				break;
-			}
-			/* Sleep 1/10 of a second */
-			usleep(100000);
-		}
+		ast_softhangup_all();
+		waited |= wait_for_channels_to_die(niceness, SHUTDOWN_TIMEOUT);
 	} else if (niceness >= SHUTDOWN_NICE) {
 		if (niceness != SHUTDOWN_REALLY_NICE) {
-			ast_begin_shutdown(0);
+			ast_begin_shutdown();
 		}
 		if (ast_opt_console) {
 			ast_verb(0, "Waiting for inactivity to perform %s...\n", restart ? "restart" : "halt");
 		}
-		for (;;) {
-			if (!ast_undestroyed_channels() || shuttingdown != niceness) {
-				break;
-			}
-			sleep(1);
-		}
+		waited |= wait_for_channels_to_die(niceness, -1);
 	}
 
 	/* Re-acquire lock and check if someone changed the niceness, in which
@@ -1944,9 +2032,28 @@ static int can_safely_quit(shutdown_nice_t niceness, int restart)
 		ast_mutex_unlock(&safe_system_lock);
 		return 0;
 	}
-	shuttingdown = SHUTTING_DOWN;
+
+	if (niceness >= SHUTDOWN_REALLY_NICE) {
+		shuttingdown = SHUTTING_DOWN;
+		ast_mutex_unlock(&safe_system_lock);
+
+		/* No more Mr. Nice guy.  We are committed to shutting down now. */
+		ast_begin_shutdown();
+		ast_softhangup_all();
+		waited |= wait_for_channels_to_die(SHUTTING_DOWN, SHUTDOWN_TIMEOUT);
+
+		ast_mutex_lock(&safe_system_lock);
+	}
+	shuttingdown = SHUTTING_DOWN_FINAL;
 	ast_mutex_unlock(&safe_system_lock);
 
+	if (niceness >= SHUTDOWN_NORMAL && waited) {
+		/*
+		 * We were not idle.  Give things in progress a chance to
+		 * recognize the final shutdown phase.
+		 */
+		sleep(1);
+	}
 	return 1;
 }
 
@@ -1962,13 +2069,7 @@ static void really_quit(int num, shutdown_nice_t niceness, int restart)
 	}
 
 	if (ast_opt_console || (ast_opt_remote && !ast_opt_exec)) {
-		char filename[80] = "";
-		if (getenv("HOME")) {
-			snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME"));
-		}
-		if (!ast_strlen_zero(filename)) {
-			ast_el_write_history(filename);
-		}
+		ast_el_write_default_histfile();
 		if (consolethread == AST_PTHREADT_NULL || consolethread == pthread_self()) {
 			/* Only end if we are the consolethread, otherwise there's a race with that thread. */
 			if (el != NULL) {
@@ -2199,7 +2300,7 @@ static void console_verboser(const char *s)
 	}
 }
 
-static int ast_all_zeros(char *s)
+static int ast_all_zeros(const char *s)
 {
 	while (*s) {
 		if (*s > 32)
@@ -2210,7 +2311,7 @@ static int ast_all_zeros(char *s)
 }
 
 /* This is the main console CLI command handler.  Run by the main() thread. */
-static void consolehandler(char *s)
+static void consolehandler(const char *s)
 {
 	printf("%s", term_end());
 	fflush(stdout);
@@ -2228,7 +2329,7 @@ static void consolehandler(char *s)
 		ast_cli_command(STDOUT_FILENO, s);
 }
 
-static int remoteconsolehandler(char *s)
+static int remoteconsolehandler(const char *s)
 {
 	int ret = 0;
 
@@ -2454,8 +2555,6 @@ static char *handle_restart_when_convenient(struct ast_cli_entry *e, int cmd, st
 
 static char *handle_abort_shutdown(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-	int aborting_shutdown = 0;
-
 	switch (cmd) {
 	case CLI_INIT:
 		e->command = "core abort shutdown";
@@ -2471,16 +2570,8 @@ static char *handle_abort_shutdown(struct ast_cli_entry *e, int cmd, struct ast_
 	if (a->argc != e->args)
 		return CLI_SHOWUSAGE;
 
-	ast_mutex_lock(&safe_system_lock);
-	if (shuttingdown >= SHUTDOWN_FAST) {
-		aborting_shutdown = 1;
-		shuttingdown = NOT_SHUTTING_DOWN;
-	}
-	ast_mutex_unlock(&safe_system_lock);
+	ast_cancel_shutdown();
 
-	if (aborting_shutdown) {
-		ast_cancel_shutdown();
-	}
 	return CLI_SUCCESS;
 }
 
@@ -3155,7 +3246,7 @@ static int ast_el_initialize(void)
 
 #define MAX_HISTORY_COMMAND_LENGTH 256
 
-static int ast_el_add_history(char *buf)
+static int ast_el_add_history(const char *buf)
 {
 	HistEvent ev;
 	char *stripped_buf;
@@ -3177,7 +3268,7 @@ static int ast_el_add_history(char *buf)
 	return history(el_hist, &ev, H_ENTER, stripped_buf);
 }
 
-static int ast_el_write_history(char *filename)
+static int ast_el_write_history(const char *filename)
 {
 	HistEvent ev;
 
@@ -3187,7 +3278,7 @@ static int ast_el_write_history(char *filename)
 	return (history(el_hist, &ev, H_SAVE, filename));
 }
 
-static int ast_el_read_history(char *filename)
+static int ast_el_read_history(const char *filename)
 {
 	HistEvent ev;
 
@@ -3198,11 +3289,32 @@ static int ast_el_read_history(char *filename)
 	return history(el_hist, &ev, H_LOAD, filename);
 }
 
+static void ast_el_read_default_histfile(void)
+{
+	char histfile[80] = "";
+	const char *home = getenv("HOME");
+
+	if (!ast_strlen_zero(home)) {
+		snprintf(histfile, sizeof(histfile), "%s/.asterisk_history", home);
+		ast_el_read_history(histfile);
+	}
+}
+
+static void ast_el_write_default_histfile(void)
+{
+	char histfile[80] = "";
+	const char *home = getenv("HOME");
+
+	if (!ast_strlen_zero(home)) {
+		snprintf(histfile, sizeof(histfile), "%s/.asterisk_history", home);
+		ast_el_write_history(histfile);
+	}
+}
+
 static void ast_remotecontrol(char *data)
 {
-	char buf[80];
+	char buf[256] = "";
 	int res;
-	char filename[80] = "";
 	char *hostname;
 	char *cpid;
 	char *version;
@@ -3212,12 +3324,16 @@ static void ast_remotecontrol(char *data)
 	char *ebuf;
 	int num = 0;
 
+	ast_term_init();
+	printf("%s", term_end());
+	fflush(stdout);
+
 	memset(&sig_flags, 0, sizeof(sig_flags));
 	signal(SIGINT, __remote_quit_handler);
 	signal(SIGTERM, __remote_quit_handler);
 	signal(SIGHUP, __remote_quit_handler);
 
-	if (read(ast_consock, buf, sizeof(buf)) < 0) {
+	if (read(ast_consock, buf, sizeof(buf) - 1) < 0) {
 		ast_log(LOG_ERROR, "read() failed: %s\n", strerror(errno));
 		return;
 	}
@@ -3310,16 +3426,12 @@ static void ast_remotecontrol(char *data)
 
 	ast_verbose("Connected to Asterisk %s currently running on %s (pid = %d)\n", version, hostname, pid);
 	remotehostname = hostname;
-	if (getenv("HOME"))
-		snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME"));
 	if (el_hist == NULL || el == NULL)
 		ast_el_initialize();
+	ast_el_read_default_histfile();
 
 	el_set(el, EL_GETCFN, ast_el_read_char);
 
-	if (!ast_strlen_zero(filename))
-		ast_el_read_history(filename);
-
 	for (;;) {
 		ebuf = (char *)el_gets(el, &num);
 
@@ -3501,7 +3613,7 @@ static void ast_readconfig(void)
 		} else if (!strcasecmp(v->name, "debug")) {
 			option_debug = 0;
 			if (sscanf(v->value, "%30d", &option_debug) != 1) {
-				option_debug = ast_true(v->value);
+				option_debug = ast_true(v->value) ? 1 : 0;
 			}
 #if HAVE_WORKING_FORK
 		/* Disable forking (-f at startup) */
@@ -3778,18 +3890,11 @@ static void main_atexit(void)
 int main(int argc, char *argv[])
 {
 	int c;
-	char filename[80] = "";
-	char hostname[MAXHOSTNAMELEN] = "";
 	char * xarg = NULL;
 	int x;
-	FILE *f;
-	sigset_t sigs;
-	int num;
 	int isroot = 1, rundir_exists = 0;
-	char *buf;
 	const char *runuser = NULL, *rungroup = NULL;
 	char *remotesock = NULL;
-	int moduleresult;         /*!< Result from the module load subsystem */
 	struct rlimit l;
 
 	/* Remember original args for restart */
@@ -3808,12 +3913,8 @@ int main(int argc, char *argv[])
 	if (argv[0] && (strstr(argv[0], "rasterisk")) != NULL) {
 		ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FORK | AST_OPT_FLAG_REMOTE);
 	}
-	if (gethostname(hostname, sizeof(hostname)-1))
-		ast_copy_string(hostname, "<Unknown>", sizeof(hostname));
 	ast_mainpid = getpid();
 
-	if (getenv("HOME"))
-		snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME"));
 	/*! \brief Check for options
 	 *
 	 * \todo Document these options
@@ -4157,6 +4258,10 @@ int main(int argc, char *argv[])
 				quit_handler(0, SHUTDOWN_FAST, 0);
 				exit(0);
 			}
+			ast_term_init();
+			printf("%s", term_end());
+			fflush(stdout);
+
 			print_intro_message(runuser, rungroup);
 			printf("%s", term_quit());
 			ast_remotecontrol(NULL);
@@ -4173,6 +4278,19 @@ int main(int argc, char *argv[])
 		exit(1);
 	}
 
+	/* Not a remote console? Start the daemon. */
+	asterisk_daemon(isroot, runuser, rungroup);
+	return 0;
+}
+
+static void asterisk_daemon(int isroot, const char *runuser, const char *rungroup)
+{
+	FILE *f;
+	sigset_t sigs;
+	int num;
+	char *buf;
+	int moduleresult;         /*!< Result from the module load subsystem */
+
 	/* This needs to remain as high up in the initial start up as possible.
 	 * daemon causes a fork to occur, which has all sorts of unintended
 	 * consequences for things that interact with threads.  This call *must*
@@ -4261,14 +4379,15 @@ int main(int argc, char *argv[])
 	register_config_cli();
 	read_config_maps();
 
-	astobj2_init();
+	if (astobj2_init()) {
+		printf("Failed: astobj2_init\n%s", term_quit());
+		exit(1);
+	}
 
 	if (ast_opt_console) {
 		if (el_hist == NULL || el == NULL)
 			ast_el_initialize();
-
-		if (!ast_strlen_zero(filename))
-			ast_el_read_history(filename);
+		ast_el_read_default_histfile();
 	}
 
 	ast_json_init();
@@ -4279,34 +4398,34 @@ int main(int argc, char *argv[])
 	ast_builtins_init();
 
 	if (ast_utils_init()) {
-		printf("%s", term_quit());
+		printf("Failed: ast_utils_init\n%s", term_quit());
 		exit(1);
 	}
 
 	if (ast_tps_init()) {
-		printf("%s", term_quit());
+		printf("Failed: ast_tps_init\n%s", term_quit());
 		exit(1);
 	}
 
 	if (ast_fd_init()) {
-		printf("%s", term_quit());
+		printf("Failed: ast_fd_init\n%s", term_quit());
 		exit(1);
 	}
 
 	if (ast_pbx_init()) {
-		printf("%s", term_quit());
+		printf("Failed: ast_pbx_init\n%s", term_quit());
 		exit(1);
 	}
 
 #ifdef TEST_FRAMEWORK
 	if (ast_test_init()) {
-		printf("%s", term_quit());
+		printf("Failed: ast_test_init\n%s", term_quit());
 		exit(1);
 	}
 #endif
 
 	if (ast_translate_init()) {
-		printf("%s", term_quit());
+		printf("Failed: ast_translate_init\n%s", term_quit());
 		exit(1);
 	}
 
@@ -4314,27 +4433,27 @@ int main(int argc, char *argv[])
 	ast_uuid_init();
 
 	if (ast_sorcery_init()) {
-		printf("%s", term_quit());
+		printf("Failed: ast_sorcery_init\n%s", term_quit());
 		exit(1);
 	}
 
 	if (ast_codec_init()) {
-		printf("%s", term_quit());
+		printf("Failed: ast_codec_init\n%s", term_quit());
 		exit(1);
 	}
 
 	if (ast_format_init()) {
-		printf("%s", term_quit());
+		printf("Failed: ast_format_init\n%s", term_quit());
 		exit(1);
 	}
 
 	if (ast_format_cache_init()) {
-		printf("%s", term_quit());
+		printf("Failed: ast_format_cache_init\n%s", term_quit());
 		exit(1);
 	}
 
 	if (ast_codec_builtin_init()) {
-		printf("%s", term_quit());
+		printf("Failed: ast_codec_builtin_init\n%s", term_quit());
 		exit(1);
 	}
 
@@ -4346,7 +4465,7 @@ int main(int argc, char *argv[])
 	aco_init();
 
 	if (ast_bucket_init()) {
-		printf("%s", term_quit());
+		printf("Failed: ast_bucket_init\n%s", term_quit());
 		exit(1);
 	}
 
@@ -4366,12 +4485,14 @@ int main(int argc, char *argv[])
 	}
 
 	ast_makesocket();
-	sigemptyset(&sigs);
-	sigaddset(&sigs, SIGHUP);
-	sigaddset(&sigs, SIGTERM);
-	sigaddset(&sigs, SIGINT);
-	sigaddset(&sigs, SIGPIPE);
-	sigaddset(&sigs, SIGWINCH);
+	/* GCC 4.9 gives a bogus "right-hand operand of comma expression has
+	 * no effect" warning */
+	(void) sigemptyset(&sigs);
+	(void) sigaddset(&sigs, SIGHUP);
+	(void) sigaddset(&sigs, SIGTERM);
+	(void) sigaddset(&sigs, SIGINT);
+	(void) sigaddset(&sigs, SIGPIPE);
+	(void) sigaddset(&sigs, SIGWINCH);
 	pthread_sigmask(SIG_BLOCK, &sigs, NULL);
 	sigaction(SIGURG, &urg_handler, NULL);
 	signal(SIGINT, __quit_handler);
@@ -4386,26 +4507,26 @@ int main(int argc, char *argv[])
 	initstate((unsigned int) getpid() * 65536 + (unsigned int) time(NULL), randompool, sizeof(randompool));
 
 	if (init_logger()) {		/* Start logging subsystem */
-		printf("%s", term_quit());
+		printf("Failed: init_logger\n%s", term_quit());
 		exit(1);
 	}
 
 	threadstorage_init();
 
 	if (ast_rtp_engine_init()) {
-		printf("%s", term_quit());
+		printf("Failed: ast_rtp_engine_init\n%s", term_quit());
 		exit(1);
 	}
 
 	ast_autoservice_init();
 
 	if (ast_timing_init()) {
-		printf("%s", term_quit());
+		printf("Failed: ast_timing_init\n%s", term_quit());
 		exit(1);
 	}
 
 	if (ast_ssl_init()) {
-		printf("%s", term_quit());
+		printf("Failed: ast_ssl_init\n%s", term_quit());
 		exit(1);
 	}
 
@@ -4420,87 +4541,87 @@ int main(int argc, char *argv[])
 	}
 
 	if (astdb_init()) {
-		printf("%s", term_quit());
+		printf("Failed: astdb_init\n%s", term_quit());
 		exit(1);
 	}
 
 	if (ast_msg_init()) {
-		printf("%s", term_quit());
+		printf("Failed: ast_msg_init\n%s", term_quit());
 		exit(1);
 	}
 
 	/* initialize the data retrieval API */
 	if (ast_data_init()) {
-		printf ("%s", term_quit());
+		printf("Failed: ast_data_init\n%s", term_quit());
 		exit(1);
 	}
 
 	ast_channels_init();
 
 	if (ast_endpoint_init()) {
-		printf ("%s", term_quit());
+		printf ("Failed: ast_endpoint_init\n%s", term_quit());
 		exit(1);
 	}
 
 	if (ast_pickup_init()) {
-		printf("%s", term_quit());
+		printf("Failed: ast_pickup_init\n%s", term_quit());
 		exit(1);
 	}
 
 	if (ast_bridging_init()) {
-		printf("%s", term_quit());
+		printf("Failed: ast_bridging_init\n%s", term_quit());
 		exit(1);
 	}
 
 	if (ast_parking_stasis_init()) {
-		printf("%s", term_quit());
+		printf("Failed: ast_parking_stasis_init\n%s", term_quit());
 		exit(1);
 	}
 
 	if (ast_device_state_engine_init()) {
-		printf("%s", term_quit());
+		printf("Failed: ast_device_state_engine_init\n%s", term_quit());
 		exit(1);
 	}
 
 	if (ast_presence_state_engine_init()) {
-		printf("%s", term_quit());
+		printf("Failed: ast_presence_state_engine_init\n%s", term_quit());
 		exit(1);
 	}
 
 	if ((moduleresult = load_modules(1))) {		/* Load modules, pre-load only */
-		printf("%s", term_quit());
+		printf("Failed: load_modules\n%s", term_quit());
 		exit(moduleresult == -2 ? 2 : 1);
 	}
 
 	if (ast_features_init()) {
-		printf("%s", term_quit());
+		printf("Failed: ast_features_init\n%s", term_quit());
 		exit(1);
 	}
 
 	if (dnsmgr_init()) {		/* Initialize the DNS manager */
-		printf("%s", term_quit());
+		printf("Failed: dnsmgr_init\n%s", term_quit());
 		exit(1);
 	}
 
 	if (ast_security_stasis_init()) {		/* Initialize Security Stasis Topic and Events */
-		printf("%s", term_quit());
+		printf("Failed: ast_security_stasis_init\n%s", term_quit());
 		exit(1);
 	}
 
 	if (ast_named_acl_init()) { /* Initialize the Named ACL system */
-		printf("%s", term_quit());
+		printf("Failed: ast_named_acl_init\n%s", term_quit());
 		exit(1);
 	}
 
 	ast_http_init();		/* Start the HTTP server, if needed */
 
 	if (ast_indications_init()) {
-		printf("%s", term_quit());
+		printf("Failed: ast_indications_init\n%s", term_quit());
 		exit(1);
 	}
 
 	if (ast_cdr_engine_init()) {
-		printf("%s", term_quit());
+		printf("Failed: ast_cdr_engine_init\n%s", term_quit());
 		exit(1);
 	}
 
@@ -4508,47 +4629,47 @@ int main(int argc, char *argv[])
 	ast_udptl_init();
 
 	if (ast_image_init()) {
-		printf("%s", term_quit());
+		printf("Failed: ast_image_init\n%s", term_quit());
 		exit(1);
 	}
 
 	if (ast_file_init()) {
-		printf("%s", term_quit());
+		printf("Failed: ast_file_init\n%s", term_quit());
 		exit(1);
 	}
 
 	if (load_pbx()) {
-		printf("%s", term_quit());
+		printf("Failed: load_pbx\n%s", term_quit());
 		exit(1);
 	}
 
 	if (ast_local_init()) {
-		printf("%s", term_quit());
+		printf("Failed: ast_local_init\n%s", term_quit());
 		exit(1);
 	}
 
 	if (ast_cel_engine_init()) {
-		printf("%s", term_quit());
+		printf("Failed: ast_cel_engine_init\n%s", term_quit());
 		exit(1);
 	}
 
 	if (init_manager()) {
-		printf("%s", term_quit());
+		printf("Failed: init_manager\n%s", term_quit());
 		exit(1);
 	}
 
 	if (ast_enum_init()) {
-		printf("%s", term_quit());
+		printf("Failed: ast_enum_init\n%s", term_quit());
 		exit(1);
 	}
 
 	if (ast_cc_init()) {
-		printf("%s", term_quit());
+		printf("Failed: ast_cc_init\n%s", term_quit());
 		exit(1);
 	}
 
 	if (ast_sounds_index_init()) {
-		printf("%s", term_quit());
+		printf("Failed: ast_sounds_index_init\n%s", term_quit());
 		exit(1);
 	}
 
@@ -4572,11 +4693,11 @@ int main(int argc, char *argv[])
 		sig_alert_pipe[0] = sig_alert_pipe[1] = -1;
 	}
 
+	ast_process_pending_reloads();
+
 	ast_set_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED);
 	publish_fully_booted();
 
-	ast_process_pending_reloads();
-
 	pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);
 
 #if defined(__AST_DEBUG_MALLOC)
@@ -4586,16 +4707,23 @@ int main(int argc, char *argv[])
 	ast_lastreloadtime = ast_startuptime = ast_tvnow();
 	ast_cli_register_multiple(cli_asterisk_shutdown, ARRAY_LEN(cli_asterisk_shutdown));
 	ast_cli_register_multiple(cli_asterisk, ARRAY_LEN(cli_asterisk));
-	ast_register_atexit(main_atexit);
+	ast_register_cleanup(main_atexit);
 
 	run_startup_commands();
 
 	ast_verb(0, COLORIZE_FMT "\n", COLORIZE(COLOR_BRGREEN, 0, "Asterisk Ready."));
 
+	logger_queue_start();
+
 	if (ast_opt_console) {
 		/* Console stuff now... */
 		/* Register our quit function */
 		char title[256];
+		char hostname[MAXHOSTNAMELEN] = "";
+
+		if (gethostname(hostname, sizeof(hostname) - 1)) {
+			ast_copy_string(hostname, "<Unknown>", sizeof(hostname));
+		}
 
 		ast_pthread_create_detached(&mon_sig_flags, NULL, monitor_sig_flags, NULL);
 
@@ -4613,30 +4741,17 @@ int main(int argc, char *argv[])
 			buf = (char *) el_gets(el, &num);
 
 			if (!buf && write(1, "", 1) < 0)
-				goto lostterm;
+				return; /* quit */
 
 			if (buf) {
 				if (buf[strlen(buf)-1] == '\n')
 					buf[strlen(buf)-1] = '\0';
 
-				consolehandler((char *)buf);
-			} else if (ast_opt_remote && (write(STDOUT_FILENO, "\nUse EXIT or QUIT to exit the asterisk console\n",
-				   strlen("\nUse EXIT or QUIT to exit the asterisk console\n")) < 0)) {
-				/* Whoa, stdout disappeared from under us... Make /dev/null's */
-				int fd;
-				fd = open("/dev/null", O_RDWR);
-				if (fd > -1) {
-					dup2(fd, STDOUT_FILENO);
-					dup2(fd, STDIN_FILENO);
-				} else
-					ast_log(LOG_WARNING, "Failed to open /dev/null to recover from dead console. Bad things will happen!\n");
-				break;
+				consolehandler(buf);
 			}
 		}
 	}
 
+	/* Stall until a quit signal is given */
 	monitor_sig_flags(NULL);
-
-lostterm:
-	return 0;
 }
diff --git a/main/astfd.c b/main/astfd.c
index 577a5c7..d2cb73a 100644
--- a/main/astfd.c
+++ b/main/astfd.c
@@ -31,7 +31,7 @@
 
 #ifdef DEBUG_FD_LEAKS
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 397110 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <stdio.h>
 #include <string.h>
@@ -48,19 +48,24 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 397110 $")
 #include "asterisk/unaligned.h"
 
 static struct fdleaks {
-	char file[40];
+	const char *callname;
 	int line;
+	unsigned int isopen:1;
+	char file[40];
 	char function[25];
-	char callname[10];
 	char callargs[60];
-	unsigned int isopen:1;
 } fdleaks[1024] = { { "", }, };
 
+/* COPY does ast_copy_string(dst, src, sizeof(dst)), except:
+ * - if it doesn't fit, it copies the value after the slash
+ *   (possibly truncated)
+ * - if there is no slash, it copies the value with the head
+ *   truncated */
 #define	COPY(dst, src)                                             \
 	do {                                                           \
 		int dlen = sizeof(dst), slen = strlen(src);                \
 		if (slen + 1 > dlen) {                                     \
-			char *slash = strrchr(src, '/');                       \
+			const char *slash = strrchr(src, '/');                 \
 			if (slash) {                                           \
 				ast_copy_string(dst, slash + 1, dlen);             \
 			} else {                                               \
@@ -72,12 +77,15 @@ static struct fdleaks {
 	} while (0)
 
 #define STORE_COMMON(offset, name, ...)     \
-	COPY(fdleaks[offset].file, file);       \
-	fdleaks[offset].line = line;            \
-	COPY(fdleaks[offset].function, func);   \
-	strcpy(fdleaks[offset].callname, name); \
-	snprintf(fdleaks[offset].callargs, sizeof(fdleaks[offset].callargs), __VA_ARGS__); \
-	fdleaks[offset].isopen = 1;
+	do { \
+		struct fdleaks *tmp = &fdleaks[offset]; \
+		COPY(tmp->file, file);      \
+		tmp->line = line;           \
+		COPY(tmp->function, func);  \
+		tmp->callname = name;       \
+		snprintf(tmp->callargs, sizeof(tmp->callargs), __VA_ARGS__); \
+		tmp->isopen = 1;            \
+	} while (0)
 
 #undef open
 int __ast_fdleak_open(const char *file, int line, const char *func, const char *path, int flags, ...)
@@ -91,7 +99,7 @@ int __ast_fdleak_open(const char *file, int line, const char *func, const char *
 		mode = va_arg(ap, int);
 		va_end(ap);
 		res = open(path, flags, mode);
-		if (res > -1 && res < (sizeof(fdleaks) / sizeof(fdleaks[0]))) {
+		if (res > -1 && res < ARRAY_LEN(fdleaks)) {
 			char sflags[80];
 			snprintf(sflags, sizeof(sflags), "O_CREAT%s%s%s%s%s%s%s%s",
 				flags & O_APPEND ? "|O_APPEND" : "",
@@ -115,7 +123,7 @@ int __ast_fdleak_open(const char *file, int line, const char *func, const char *
 		}
 	} else {
 		res = open(path, flags);
-		if (res > -1 && res < (sizeof(fdleaks) / sizeof(fdleaks[0]))) {
+		if (res > -1 && res < ARRAY_LEN(fdleaks)) {
 			STORE_COMMON(res, "open", "\"%s\",%d", path, flags);
 		}
 	}
@@ -130,7 +138,9 @@ int __ast_fdleak_pipe(int *fds, const char *file, int line, const char *func)
 		return res;
 	}
 	for (i = 0; i < 2; i++) {
-		STORE_COMMON(fds[i], "pipe", "{%d,%d}", fds[0], fds[1]);
+		if (fds[i] > -1 && fds[i] < ARRAY_LEN(fdleaks)) {
+			STORE_COMMON(fds[i], "pipe", "{%d,%d}", fds[0], fds[1]);
+		}
 	}
 	return 0;
 }
@@ -141,7 +151,7 @@ int __ast_fdleak_socket(int domain, int type, int protocol, const char *file, in
 	char sdomain[20], stype[20], *sproto = NULL;
 	struct protoent *pe;
 	int res = socket(domain, type, protocol);
-	if (res < 0 || res > 1023) {
+	if (res < 0 || res >= ARRAY_LEN(fdleaks)) {
 		return res;
 	}
 
@@ -183,7 +193,7 @@ int __ast_fdleak_socket(int domain, int type, int protocol, const char *file, in
 int __ast_fdleak_close(int fd)
 {
 	int res = close(fd);
-	if (!res && fd > -1 && fd < 1024) {
+	if (!res && fd > -1 && fd < ARRAY_LEN(fdleaks)) {
 		fdleaks[fd].isopen = 0;
 	}
 	return res;
@@ -198,7 +208,9 @@ FILE *__ast_fdleak_fopen(const char *path, const char *mode, const char *file, i
 		return res;
 	}
 	fd = fileno(res);
-	STORE_COMMON(fd, "fopen", "\"%s\",\"%s\"", path, mode);
+	if (fd > -1 && fd < ARRAY_LEN(fdleaks)) {
+		STORE_COMMON(fd, "fopen", "\"%s\",\"%s\"", path, mode);
+	}
 	return res;
 }
 
@@ -211,7 +223,7 @@ int __ast_fdleak_fclose(FILE *ptr)
 	}
 
 	fd = fileno(ptr);
-	if ((res = fclose(ptr)) || fd < 0 || fd > 1023) {
+	if ((res = fclose(ptr)) || fd < 0 || fd >= ARRAY_LEN(fdleaks)) {
 		return res;
 	}
 	fdleaks[fd].isopen = 0;
@@ -222,10 +234,13 @@ int __ast_fdleak_fclose(FILE *ptr)
 int __ast_fdleak_dup2(int oldfd, int newfd, const char *file, int line, const char *func)
 {
 	int res = dup2(oldfd, newfd);
-	if (res < 0 || res > 1023) {
+	if (res < 0 || res >= ARRAY_LEN(fdleaks)) {
 		return res;
 	}
-	STORE_COMMON(res, "dup2", "%d,%d", oldfd, newfd);
+	/* On success, newfd will be closed automatically if it was already
+	 * open. We don't need to mention anything about that, we're updating
+	 * the value anway. */
+	STORE_COMMON(res, "dup2", "%d,%d", oldfd, newfd); /* res == newfd */
 	return res;
 }
 
@@ -233,7 +248,7 @@ int __ast_fdleak_dup2(int oldfd, int newfd, const char *file, int line, const ch
 int __ast_fdleak_dup(int oldfd, const char *file, int line, const char *func)
 {
 	int res = dup(oldfd);
-	if (res < 0 || res > 1023) {
+	if (res < 0 || res >= ARRAY_LEN(fdleaks)) {
 		return res;
 	}
 	STORE_COMMON(res, "dup2", "%d", oldfd);
@@ -263,7 +278,7 @@ static char *handle_show_fd(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
 		snprintf(line, sizeof(line), "%d/%d", (int) rl.rlim_cur, (int) rl.rlim_max);
 	}
 	ast_cli(a->fd, "Current maxfiles: %s\n", line);
-	for (i = 0; i < 1024; i++) {
+	for (i = 0; i < ARRAY_LEN(fdleaks); i++) {
 		if (fdleaks[i].isopen) {
 			snprintf(line, sizeof(line), "%d", fdleaks[i].line);
 			ast_cli(a->fd, "%5d %15s:%-7.7s (%-25s): %s(%s)\n", i, fdleaks[i].file, line, fdleaks[i].function, fdleaks[i].callname, fdleaks[i].callargs);
@@ -281,7 +296,7 @@ static void fd_shutdown(void)
 
 int ast_fd_init(void)
 {
-	ast_register_atexit(fd_shutdown);
+	ast_register_cleanup(fd_shutdown);
 	return ast_cli_register(&cli_show_fd);
 }
 
diff --git a/main/astmm.c b/main/astmm.c
index 262ab45..5812174 100644
--- a/main/astmm.c
+++ b/main/astmm.c
@@ -32,7 +32,7 @@
 
 #if defined(__AST_DEBUG_MALLOC)
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 398732 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/paths.h"	/* use ast_config_AST_LOG_DIR */
 #include <stddef.h>
@@ -1493,7 +1493,7 @@ void __ast_mm_init_phase_2(void)
 		ast_log(LOG_ERROR, "Could not open malloc debug log file: %s\n", filename);
 	}
 
-	ast_register_atexit(mm_atexit_ast);
+	ast_register_cleanup(mm_atexit_ast);
 }
 
 #endif	/* defined(__AST_DEBUG_MALLOC) */
diff --git a/main/astobj2.c b/main/astobj2.c
index 39b2a64..1bb5237 100644
--- a/main/astobj2.c
+++ b/main/astobj2.c
@@ -27,7 +27,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 423418 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/_private.h"
 #include "asterisk/astobj2.h"
@@ -36,6 +36,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 423418 $")
 #include "asterisk/cli.h"
 #include "asterisk/paths.h"
 
+/* Use ast_log_safe in place of ast_log. */
+#define ast_log ast_log_safe
+
 static FILE *ref_log;
 
 /*!
@@ -896,13 +899,7 @@ int astobj2_init(void)
 {
 #ifdef REF_DEBUG
 	char ref_filename[1024];
-#endif
-
-	if (container_init() != 0) {
-		return -1;
-	}
 
-#ifdef REF_DEBUG
 	snprintf(ref_filename, sizeof(ref_filename), "%s/refs", ast_config_AST_LOG_DIR);
 	ref_log = fopen(ref_filename, "w");
 	if (!ref_log) {
@@ -910,11 +907,16 @@ int astobj2_init(void)
 	}
 #endif
 
+	if (container_init() != 0) {
+		fclose(ref_log);
+		return -1;
+	}
+
 #if defined(AO2_DEBUG)
 	ast_cli_register_multiple(cli_astobj2, ARRAY_LEN(cli_astobj2));
 #endif	/* defined(AO2_DEBUG) */
 
-	ast_register_atexit(astobj2_cleanup);
+	ast_register_cleanup(astobj2_cleanup);
 
 	return 0;
 }
diff --git a/main/astobj2_container.c b/main/astobj2_container.c
index 918d1e0..5a27a0a 100644
--- a/main/astobj2_container.c
+++ b/main/astobj2_container.c
@@ -22,7 +22,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 417213 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/_private.h"
 #include "asterisk/astobj2.h"
@@ -510,6 +510,12 @@ struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags)
 
 void ao2_iterator_restart(struct ao2_iterator *iter)
 {
+	if (!is_ao2_object(iter->c)) {
+		ast_log(LOG_ERROR, "Iterator container is not valid.\n");
+		ast_assert(0);
+		return;
+	}
+
 	/* Release the last container node reference if we have one. */
 	if (iter->last_node) {
 		enum ao2_lock_req orig_lock;
@@ -1211,7 +1217,7 @@ int container_init(void)
 	}
 
 	ast_cli_register_multiple(cli_astobj2, ARRAY_LEN(cli_astobj2));
-	ast_register_atexit(container_cleanup);
+	ast_register_cleanup(container_cleanup);
 #endif	/* defined(AO2_DEBUG) */
 
 	return 0;
diff --git a/main/astobj2_hash.c b/main/astobj2_hash.c
index 08e01bd..1cd6ee2 100644
--- a/main/astobj2_hash.c
+++ b/main/astobj2_hash.c
@@ -23,7 +23,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 416807 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/_private.h"
 #include "asterisk/astobj2.h"
@@ -186,6 +186,8 @@ static void hash_ao2_node_destructor(void *v_doomed)
 		 * same node.
 		 */
 		my_container = (struct ao2_container_hash *) doomed->common.my_container;
+		ast_assert(is_ao2_object(my_container));
+
 		__adjust_lock(my_container, AO2_LOCK_REQ_WRLOCK, 1);
 
 #if defined(AO2_DEBUG)
@@ -233,8 +235,7 @@ static struct hash_bucket_node *hash_ao2_new_node(struct ao2_container_hash *sel
 		return NULL;
 	}
 
-	i = abs(self->hash_fn(obj_new, OBJ_SEARCH_OBJECT));
-	i %= self->n_buckets;
+	i = abs(self->hash_fn(obj_new, OBJ_SEARCH_OBJECT) % self->n_buckets);
 
 	if (tag) {
 		__ao2_ref_debug(obj_new, +1, tag, file, line, func);
@@ -384,8 +385,8 @@ static struct hash_bucket_node *hash_ao2_find_first(struct ao2_container_hash *s
 	case OBJ_SEARCH_OBJECT:
 	case OBJ_SEARCH_KEY:
 		/* we know hash can handle this case */
-		bucket_cur = abs(self->hash_fn(arg, flags & OBJ_SEARCH_MASK));
-		bucket_cur %= self->n_buckets;
+		bucket_cur = abs(self->hash_fn(arg, flags & OBJ_SEARCH_MASK)
+				% self->n_buckets);
 		state->sort_fn = self->common.sort_fn;
 		break;
 	case OBJ_SEARCH_PARTIAL_KEY:
@@ -982,8 +983,8 @@ static int hash_ao2_integrity(struct ao2_container_hash *self)
 			++count_obj;
 
 			/* Check container hash key for expected bucket. */
-			bucket_exp = abs(self->hash_fn(node->common.obj, OBJ_SEARCH_OBJECT));
-			bucket_exp %= self->n_buckets;
+			bucket_exp = abs(self->hash_fn(node->common.obj, OBJ_SEARCH_OBJECT)
+					% self->n_buckets);
 			if (bucket != bucket_exp) {
 				ast_log(LOG_ERROR, "Bucket %d node hashes to bucket %d!\n",
 					bucket, bucket_exp);
diff --git a/main/astobj2_rbtree.c b/main/astobj2_rbtree.c
index 8599078..d8195d4 100644
--- a/main/astobj2_rbtree.c
+++ b/main/astobj2_rbtree.c
@@ -23,7 +23,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 416807 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/_private.h"
 #include "asterisk/astobj2.h"
@@ -878,6 +878,8 @@ static void rb_ao2_node_destructor(void *v_doomed)
 		 * same node.
 		 */
 		my_container = (struct ao2_container_rbtree *) doomed->common.my_container;
+		ast_assert(is_ao2_object(my_container));
+
 		__adjust_lock(my_container, AO2_LOCK_REQ_WRLOCK, 1);
 
 #if defined(AO2_DEBUG)
diff --git a/main/audiohook.c b/main/audiohook.c
index 01b5ee0..5dfbb5d 100644
--- a/main/audiohook.c
+++ b/main/audiohook.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 426803 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <signal.h>
 
@@ -46,6 +46,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 426803 $")
 #define AST_AUDIOHOOK_SYNC_TOLERANCE 100 /*!< Tolerance in milliseconds for audiohooks synchronization */
 #define AST_AUDIOHOOK_SMALL_QUEUE_TOLERANCE 100 /*!< When small queue is enabled, this is the maximum amount of audio that can remain queued at a time. */
 
+#define DEFAULT_INTERNAL_SAMPLE_RATE 8000
+
 struct ast_audiohook_translate {
 	struct ast_trans_pvt *trans_pvt;
 	struct ast_format *format;
@@ -117,7 +119,7 @@ int ast_audiohook_init(struct ast_audiohook *audiohook, enum ast_audiohook_type
 	audiohook->init_flags = init_flags;
 
 	/* initialize internal rate at 8khz, this will adjust if necessary */
-	audiohook_set_internal_rate(audiohook, 8000, 0);
+	audiohook_set_internal_rate(audiohook, DEFAULT_INTERNAL_SAMPLE_RATE, 0);
 
 	/* Since we are just starting out... this audiohook is new */
 	ast_audiohook_update_status(audiohook, AST_AUDIOHOOK_STATUS_NEW);
@@ -251,11 +253,16 @@ static struct ast_frame *audiohook_read_frame_single(struct ast_audiohook *audio
 
 static struct ast_frame *audiohook_read_frame_both(struct ast_audiohook *audiohook, size_t samples, struct ast_frame **read_reference, struct ast_frame **write_reference)
 {
-	int i = 0, usable_read, usable_write;
-	short buf1[samples], buf2[samples], *read_buf = NULL, *write_buf = NULL, *final_buf = NULL, *data1 = NULL, *data2 = NULL;
+	int count;
+	int usable_read;
+	int usable_write;
+	short adjust_value;
+	short buf1[samples];
+	short buf2[samples];
+	short *read_buf = NULL;
+	short *write_buf = NULL;
 	struct ast_frame frame = {
 		.frametype = AST_FRAME_VOICE,
-		.data.ptr = NULL,
 		.datalen = sizeof(buf1),
 		.samples = samples,
 	};
@@ -288,8 +295,7 @@ static struct ast_frame *audiohook_read_frame_both(struct ast_audiohook *audioho
 			read_buf = buf1;
 			/* Adjust read volume if need be */
 			if (audiohook->options.read_volume) {
-				int count = 0;
-				short adjust_value = abs(audiohook->options.read_volume);
+				adjust_value = abs(audiohook->options.read_volume);
 				for (count = 0; count < samples; count++) {
 					if (audiohook->options.read_volume > 0) {
 						ast_slinear_saturated_multiply(&buf1[count], &adjust_value);
@@ -309,8 +315,7 @@ static struct ast_frame *audiohook_read_frame_both(struct ast_audiohook *audioho
 			write_buf = buf2;
 			/* Adjust write volume if need be */
 			if (audiohook->options.write_volume) {
-				int count = 0;
-				short adjust_value = abs(audiohook->options.write_volume);
+				adjust_value = abs(audiohook->options.write_volume);
 				for (count = 0; count < samples; count++) {
 					if (audiohook->options.write_volume > 0) {
 						ast_slinear_saturated_multiply(&buf2[count], &adjust_value);
@@ -324,34 +329,32 @@ static struct ast_frame *audiohook_read_frame_both(struct ast_audiohook *audioho
 		ast_debug(1, "Failed to get %d samples from write factory %p\n", (int)samples, &audiohook->write_factory);
 	}
 
+	frame.subclass.format = ast_format_cache_get_slin_by_rate(audiohook->hook_internal_samp_rate);
+
 	/* Basically we figure out which buffer to use... and if mixing can be done here */
 	if (read_buf && read_reference) {
-		frame.data.ptr = buf1;
+		frame.data.ptr = read_buf;
 		*read_reference = ast_frdup(&frame);
 	}
 	if (write_buf && write_reference) {
-		frame.data.ptr = buf2;
+		frame.data.ptr = write_buf;
 		*write_reference = ast_frdup(&frame);
 	}
 
-	if (read_buf && write_buf) {
-		for (i = 0, data1 = read_buf, data2 = write_buf; i < samples; i++, data1++, data2++) {
-			ast_slinear_saturated_add(data1, data2);
+	/* Make the correct buffer part of the built frame, so it gets duplicated. */
+	if (read_buf) {
+		frame.data.ptr = read_buf;
+		if (write_buf) {
+			for (count = 0; count < samples; count++) {
+				ast_slinear_saturated_add(read_buf++, write_buf++);
+			}
 		}
-		final_buf = buf1;
-	} else if (read_buf) {
-		final_buf = buf1;
 	} else if (write_buf) {
-		final_buf = buf2;
+		frame.data.ptr = write_buf;
 	} else {
 		return NULL;
 	}
 
-	/* Make the final buffer part of the frame, so it gets duplicated fine */
-	frame.data.ptr = final_buf;
-
-	frame.subclass.format = ast_format_cache_get_slin_by_rate(audiohook->hook_internal_samp_rate);
-
 	/* Yahoo, a combined copy of the audio! */
 	return ast_frdup(&frame);
 }
@@ -360,21 +363,34 @@ static struct ast_frame *audiohook_read_frame_helper(struct ast_audiohook *audio
 {
 	struct ast_frame *read_frame = NULL, *final_frame = NULL;
 	struct ast_format *slin;
-	int samples_converted;
-
-	/* the number of samples requested is based on the format they are requesting.  Inorder
-	 * to process this correctly samples must be converted to our internal sample rate */
-	if (audiohook->hook_internal_samp_rate == ast_format_get_sample_rate(format)) {
-		samples_converted = samples;
-	} else if (audiohook->hook_internal_samp_rate > ast_format_get_sample_rate(format)) {
-		samples_converted = samples * (audiohook->hook_internal_samp_rate / (float) ast_format_get_sample_rate(format));
-	} else {
-		samples_converted = samples * (ast_format_get_sample_rate(format) / (float) audiohook->hook_internal_samp_rate);
+
+	/*
+	 * Update the rate if compatibility mode is turned off or if it is
+	 * turned on and the format rate is higher than the current rate.
+	 *
+	 * This makes it so any unnecessary rate switching/resetting does
+	 * not take place and also any associated audiohook_list's internal
+	 * sample rate maintains the highest sample rate between hooks.
+	 */
+	if (!ast_test_flag(audiohook, AST_AUDIOHOOK_COMPATIBLE) ||
+	    (ast_test_flag(audiohook, AST_AUDIOHOOK_COMPATIBLE) &&
+	      ast_format_get_sample_rate(format) > audiohook->hook_internal_samp_rate)) {
+		audiohook_set_internal_rate(audiohook, ast_format_get_sample_rate(format), 1);
+	}
+
+	/* If the sample rate of the requested format differs from that of the underlying audiohook
+	 * sample rate determine how many samples we actually need to get from the audiohook. This
+	 * needs to occur as the signed linear factory stores them at the rate of the audiohook.
+	 * We do this by determining the duration of audio they've requested and then determining
+	 * how many samples that would be in the audiohook format.
+	 */
+	if (ast_format_get_sample_rate(format) != audiohook->hook_internal_samp_rate) {
+		samples = (audiohook->hook_internal_samp_rate / 1000) * (samples / (ast_format_get_sample_rate(format) / 1000));
 	}
 
 	if (!(read_frame = (direction == AST_AUDIOHOOK_DIRECTION_BOTH ?
-		audiohook_read_frame_both(audiohook, samples_converted, read_reference, write_reference) :
-		audiohook_read_frame_single(audiohook, samples_converted, direction)))) {
+		audiohook_read_frame_both(audiohook, samples, read_reference, write_reference) :
+		audiohook_read_frame_single(audiohook, samples, direction)))) {
 		return NULL;
 	}
 
@@ -434,6 +450,22 @@ struct ast_frame *ast_audiohook_read_frame_all(struct ast_audiohook *audiohook,
 static void audiohook_list_set_samplerate_compatibility(struct ast_audiohook_list *audiohook_list)
 {
 	struct ast_audiohook *ah = NULL;
+
+	/*
+	 * Anytime the samplerate compatibility is set (attach/remove an audiohook) the
+	 * list's internal sample rate needs to be reset so that the next time processing
+	 * through write_list, if needed, it will get updated to the correct rate.
+	 *
+	 * A list's internal rate always chooses the higher between its own rate and a
+	 * given rate. If the current rate is being driven by an audiohook that wanted a
+	 * higher rate then when this audiohook is removed the list's rate would remain
+	 * at that level when it should be lower, and with no way to lower it since any
+	 * rate compared against it would be lower.
+	 *
+	 * By setting it back to the lowest rate it can recalulate the new highest rate.
+	 */
+	audiohook_list->list_internal_samp_rate = DEFAULT_INTERNAL_SAMPLE_RATE;
+
 	audiohook_list->native_slin_compatible = 1;
 	AST_LIST_TRAVERSE(&audiohook_list->manipulate_list, ah, list) {
 		if (!(ah->init_flags & AST_AUDIOHOOK_MANIPULATE_ALL_RATES)) {
@@ -464,7 +496,7 @@ int ast_audiohook_attach(struct ast_channel *chan, struct ast_audiohook *audioho
 		AST_LIST_HEAD_INIT_NOLOCK(&ast_channel_audiohooks(chan)->whisper_list);
 		AST_LIST_HEAD_INIT_NOLOCK(&ast_channel_audiohooks(chan)->manipulate_list);
 		/* This sample rate will adjust as necessary when writing to the list. */
-		ast_channel_audiohooks(chan)->list_internal_samp_rate = 8000;
+		ast_channel_audiohooks(chan)->list_internal_samp_rate = DEFAULT_INTERNAL_SAMPLE_RATE;
 	}
 
 	/* Drop into respective list */
@@ -476,8 +508,11 @@ int ast_audiohook_attach(struct ast_channel *chan, struct ast_audiohook *audioho
 		AST_LIST_INSERT_TAIL(&ast_channel_audiohooks(chan)->manipulate_list, audiohook, list);
 	}
 
-
-	audiohook_set_internal_rate(audiohook, ast_channel_audiohooks(chan)->list_internal_samp_rate, 1);
+	/*
+	 * Initialize the audiohook's rate to the default. If it needs to be,
+	 * it will get updated later.
+	 */
+	audiohook_set_internal_rate(audiohook, DEFAULT_INTERNAL_SAMPLE_RATE, 1);
 	audiohook_list_set_samplerate_compatibility(ast_channel_audiohooks(chan));
 
 	/* Change status over to running since it is now attached */
@@ -775,27 +810,34 @@ static struct ast_frame *audiohook_list_translate_to_slin(struct ast_audiohook_l
 	struct ast_frame *new_frame = frame;
 	struct ast_format *slin;
 
-	/* If we are capable of maintaining doing samplerates other that 8khz, update
-	 * the internal audiohook_list's rate and higher samplerate audio arrives. By
-	 * updating the list's rate, all the audiohooks in the list will be updated as well
-	 * as the are written and read from. */
-	if (audiohook_list->native_slin_compatible) {
-		audiohook_list->list_internal_samp_rate =
-			MAX(ast_format_get_sample_rate(frame->subclass.format), audiohook_list->list_internal_samp_rate);
-	}
+	/*
+	 * If we are capable of sample rates other that 8khz, update the internal
+	 * audiohook_list's rate and higher sample rate audio arrives. If native
+	 * slin compatibility is turned on all audiohooks in the list will be
+	 * updated as well during read/write processing.
+	 */
+	audiohook_list->list_internal_samp_rate =
+		MAX(ast_format_get_sample_rate(frame->subclass.format), audiohook_list->list_internal_samp_rate);
 
 	slin = ast_format_cache_get_slin_by_rate(audiohook_list->list_internal_samp_rate);
 	if (ast_format_cmp(frame->subclass.format, slin) == AST_FORMAT_CMP_EQUAL) {
 		return new_frame;
 	}
 
-	if (ast_format_cmp(frame->subclass.format, in_translate->format) == AST_FORMAT_CMP_NOT_EQUAL) {
+	if (!in_translate->format ||
+		ast_format_cmp(frame->subclass.format, in_translate->format) != AST_FORMAT_CMP_EQUAL) {
+		struct ast_trans_pvt *new_trans;
+
+		new_trans = ast_translator_build_path(slin, frame->subclass.format);
+		if (!new_trans) {
+			return NULL;
+		}
+
 		if (in_translate->trans_pvt) {
 			ast_translator_free_path(in_translate->trans_pvt);
 		}
-		if (!(in_translate->trans_pvt = ast_translator_build_path(slin, frame->subclass.format))) {
-			return NULL;
-		}
+		in_translate->trans_pvt = new_trans;
+
 		ao2_replace(in_translate->format, frame->subclass.format);
 	}
 
@@ -831,6 +873,36 @@ static struct ast_frame *audiohook_list_translate_to_native(struct ast_audiohook
 }
 
 /*!
+ *\brief Set the audiohook's internal sample rate to the audiohook_list's rate,
+ *       but only when native slin compatibility is turned on.
+ *
+ * \param audiohook_list audiohook_list data object
+ * \param audiohook the audiohook to update
+ * \param rate the current max internal sample rate
+ */
+static void audiohook_list_set_hook_rate(struct ast_audiohook_list *audiohook_list,
+					 struct ast_audiohook *audiohook, int *rate)
+{
+	/* The rate should always be the max between itself and the hook */
+	if (audiohook->hook_internal_samp_rate > *rate) {
+		*rate = audiohook->hook_internal_samp_rate;
+	}
+
+	/*
+	 * If native slin compatibility is turned on then update the audiohook
+	 * with the audiohook_list's current rate. Note, the audiohook's rate is
+	 * set to the audiohook_list's rate and not the given rate. If there is
+	 * a change in rate the hook's rate is changed on its next check.
+	 */
+	if (audiohook_list->native_slin_compatible) {
+		ast_set_flag(audiohook, AST_AUDIOHOOK_COMPATIBLE);
+		audiohook_set_internal_rate(audiohook, audiohook_list->list_internal_samp_rate, 1);
+	} else {
+		ast_clear_flag(audiohook, AST_AUDIOHOOK_COMPATIBLE);
+	}
+}
+
+/*!
  * \brief Pass an AUDIO frame off to be handled by the audiohook core
  *
  * \details
@@ -860,6 +932,7 @@ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, st
 	int samples;
 	int middle_frame_manipulated = 0;
 	int removed = 0;
+	int internal_sample_rate;
 
 	/* ---Part_1. translate start_frame to SLINEAR if necessary. */
 	if (!(middle_frame = audiohook_list_translate_to_slin(audiohook_list, direction, start_frame))) {
@@ -867,6 +940,19 @@ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, st
 	}
 	samples = middle_frame->samples;
 
+	/*
+	 * While processing each audiohook check to see if the internal sample rate needs
+	 * to be adjusted (it should be the highest rate specified between formats and
+	 * hooks). The given audiohook_list's internal sample rate is then set to the
+	 * updated value before returning.
+	 *
+	 * If slin compatibility mode is turned on then an audiohook's internal sample
+	 * rate is set to its audiohook_list's rate. If an audiohook_list's rate is
+	 * adjusted during this pass then the change is picked up by the audiohooks
+	 * on the next pass.
+	 */
+	internal_sample_rate = audiohook_list->list_internal_samp_rate;
+
 	/* ---Part_2: Send middle_frame to spy and manipulator lists.  middle_frame is guaranteed to be SLINEAR here.*/
 	/* Queue up signed linear frame to each spy */
 	AST_LIST_TRAVERSE_SAFE_BEGIN(&audiohook_list->spy_list, audiohook, list) {
@@ -881,7 +967,7 @@ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, st
 			}
 			continue;
 		}
-		audiohook_set_internal_rate(audiohook, audiohook_list->list_internal_samp_rate, 1);
+		audiohook_list_set_hook_rate(audiohook_list, audiohook, &internal_sample_rate);
 		ast_audiohook_write_frame(audiohook, direction, middle_frame);
 		ast_audiohook_unlock(audiohook);
 	}
@@ -905,7 +991,7 @@ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, st
 				}
 				continue;
 			}
-			audiohook_set_internal_rate(audiohook, audiohook_list->list_internal_samp_rate, 1);
+			audiohook_list_set_hook_rate(audiohook_list, audiohook, &internal_sample_rate);
 			if (ast_slinfactory_available(factory) >= samples && ast_slinfactory_read(factory, read_buf, samples)) {
 				/* Take audio from this whisper source and combine it into our main buffer */
 				for (i = 0, data1 = combine_buf, data2 = read_buf; i < samples; i++, data1++, data2++) {
@@ -938,13 +1024,18 @@ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, st
 				}
 				continue;
 			}
-			audiohook_set_internal_rate(audiohook, audiohook_list->list_internal_samp_rate, 1);
-			/* Feed in frame to manipulation. */
+			audiohook_list_set_hook_rate(audiohook_list, audiohook, &internal_sample_rate);
+			/*
+			 * Feed in frame to manipulation.
+			 */
 			if (!audiohook->manipulate_callback(audiohook, chan, middle_frame, direction)) {
-				/* If the manipulation fails then the frame will be returned in its original state.
+				/*
+				 * XXX FAILURES ARE IGNORED XXX
+				 * If the manipulation fails then the frame will be returned in its original state.
 				 * Since there are potentially more manipulator callbacks in the list, no action should
-				 * be taken here to exit early. */
-				 middle_frame_manipulated = 1;
+				 * be taken here to exit early.
+				 */
+				middle_frame_manipulated = 1;
 			}
 			ast_audiohook_unlock(audiohook);
 		}
@@ -969,6 +1060,12 @@ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, st
 	/* Before returning, if an audiohook got removed, reset samplerate compatibility */
 	if (removed) {
 		audiohook_list_set_samplerate_compatibility(audiohook_list);
+	} else {
+		/*
+		 * Set the audiohook_list's rate to the updated rate. Note that if a hook
+		 * was removed then the list's internal rate is reset to the default.
+		 */
+		audiohook_list->list_internal_samp_rate = internal_sample_rate;
 	}
 
 	return end_frame;
diff --git a/main/autochan.c b/main/autochan.c
index 6a0fcd3..7da249a 100644
--- a/main/autochan.c
+++ b/main/autochan.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 369013 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/autochan.h"
 #include "asterisk/utils.h"
diff --git a/main/autoservice.c b/main/autoservice.c
index 3a4a91b..305ab23 100644
--- a/main/autoservice.c
+++ b/main/autoservice.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 415466 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/time.h>
 #include <signal.h>
diff --git a/main/backtrace.c b/main/backtrace.c
index bf976ba..a115646 100644
--- a/main/backtrace.c
+++ b/main/backtrace.c
@@ -27,7 +27,7 @@
  ***/
 
 #include "asterisk.h"
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 397570 $");
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
 
 #include "asterisk/backtrace.h"
 #include "asterisk/utils.h"
diff --git a/main/bridge.c b/main/bridge.c
index 3ddf407..b5c5951 100644
--- a/main/bridge.c
+++ b/main/bridge.c
@@ -71,7 +71,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428602 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/logger.h"
 #include "asterisk/channel.h"
@@ -129,7 +129,6 @@ static unsigned int optimization_id;
 #define ATTENDEDTRANSFER "ATTENDEDTRANSFER"
 
 static void cleanup_video_mode(struct ast_bridge *bridge);
-static int bridge_make_compatible(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel);
 
 /*! Default DTMF keys for built in features */
 static char builtin_features_dtmf[AST_BRIDGE_BUILTIN_END][MAXIMUM_DTMF_FEATURE_STRING];
@@ -415,9 +414,6 @@ static void bridge_reconfigured_connected_line_update(struct ast_bridge *bridge)
  */
 static void bridge_channel_complete_join(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
 {
-	/* Make the channel compatible with the bridge */
-	bridge_make_compatible(bridge, bridge_channel);
-
 	/* Tell the bridge technology we are joining so they set us up */
 	ast_debug(1, "Bridge %s: %p(%s) is joining %s technology\n",
 		bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan),
@@ -904,6 +900,26 @@ static int bridge_base_get_merge_priority(struct ast_bridge *self)
 	return 0;
 }
 
+/*!
+ * \internal
+ * \brief ast_bridge base push_peek method.
+ * \since 13.2.0
+ *
+ * \param self Bridge to operate upon.
+ * \param bridge_channel Bridge channel to push.
+ * \param swap Bridge channel to swap places with if not NULL.
+ *
+ * \note On entry, self is already locked.
+ * \note Stub because of nothing to do.
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+static int bridge_base_push_peek(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
+{
+	return 0;
+}
+
 struct ast_bridge_methods ast_bridge_base_v_table = {
 	.name = "base",
 	.destroy = bridge_base_destroy,
@@ -912,6 +928,7 @@ struct ast_bridge_methods ast_bridge_base_v_table = {
 	.pull = bridge_base_pull,
 	.notify_masquerade = bridge_base_notify_masquerade,
 	.get_merge_priority = bridge_base_get_merge_priority,
+	.push_peek = bridge_base_push_peek,
 };
 
 struct ast_bridge *ast_bridge_base_new(uint32_t capabilities, unsigned int flags, const char *creator, const char *name, const char *id)
@@ -936,74 +953,6 @@ int ast_bridge_destroy(struct ast_bridge *bridge, int cause)
 	return 0;
 }
 
-static int bridge_make_compatible(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
-{
-	struct ast_str *codec_buf = ast_str_alloca(64);
-	struct ast_format *best_format;
-	RAII_VAR(struct ast_format *, read_format, NULL, ao2_cleanup);
-	RAII_VAR(struct ast_format *, write_format, NULL, ao2_cleanup);
-
-	ast_channel_lock(bridge_channel->chan);
-	read_format = ao2_bump(ast_channel_readformat(bridge_channel->chan));
-	write_format = ao2_bump(ast_channel_writeformat(bridge_channel->chan));
-	ast_channel_unlock(bridge_channel->chan);
-
-	/* Are the formats currently in use something this bridge can handle? */
-	if (ast_format_cap_iscompatible_format(bridge->technology->format_capabilities, read_format) == AST_FORMAT_CMP_NOT_EQUAL) {
-		best_format = ast_format_cap_get_format(bridge->technology->format_capabilities, 0);
-
-		/* Read format is a no go... */
-		ast_debug(1, "Bridge technology %s wants to read any of formats %s but channel has %s\n",
-			bridge->technology->name,
-			ast_format_cap_get_names(bridge->technology->format_capabilities, &codec_buf),
-			ast_format_get_name(read_format));
-
-		/* Switch read format to the best one chosen */
-		if (ast_set_read_format(bridge_channel->chan, best_format)) {
-			ast_log(LOG_WARNING, "Failed to set channel %s to read format %s\n",
-				ast_channel_name(bridge_channel->chan), ast_format_get_name(best_format));
-			ao2_cleanup(best_format);
-			return -1;
-		}
-		ast_debug(1, "Bridge %s put channel %s into read format %s\n",
-			bridge->uniqueid, ast_channel_name(bridge_channel->chan),
-			ast_format_get_name(best_format));
-		ao2_cleanup(best_format);
-	} else {
-		ast_debug(1, "Bridge %s is happy that channel %s already has read format %s\n",
-			bridge->uniqueid, ast_channel_name(bridge_channel->chan),
-			ast_format_get_name(read_format));
-	}
-
-	if (ast_format_cap_iscompatible_format(bridge->technology->format_capabilities, write_format) == AST_FORMAT_CMP_NOT_EQUAL) {
-		best_format = ast_format_cap_get_format(bridge->technology->format_capabilities, 0);
-
-		/* Write format is a no go... */
-		ast_debug(1, "Bridge technology %s wants to write any of formats %s but channel has %s\n",
-			bridge->technology->name,
-			ast_format_cap_get_names(bridge->technology->format_capabilities, &codec_buf),
-			ast_format_get_name(write_format));
-
-		/* Switch write format to the best one chosen */
-		if (ast_set_write_format(bridge_channel->chan, best_format)) {
-			ast_log(LOG_WARNING, "Failed to set channel %s to write format %s\n",
-				ast_channel_name(bridge_channel->chan), ast_format_get_name(best_format));
-			ao2_cleanup(best_format);
-			return -1;
-		}
-		ast_debug(1, "Bridge %s put channel %s into write format %s\n",
-			bridge->uniqueid, ast_channel_name(bridge_channel->chan),
-			ast_format_get_name(best_format));
-		ao2_cleanup(best_format);
-	} else {
-		ast_debug(1, "Bridge %s is happy that channel %s already has write format %s\n",
-			bridge->uniqueid, ast_channel_name(bridge_channel->chan),
-			ast_format_get_name(write_format));
-	}
-
-	return 0;
-}
-
 /*!
  * \internal
  * \brief Perform the smart bridge operation.
@@ -1264,7 +1213,7 @@ static void check_bridge_play_sounds(struct ast_bridge *bridge)
 	}
 }
 
-static void update_bridge_vars_set(struct ast_channel *chan, const char *name, const char *pvtid)
+void ast_bridge_vars_set(struct ast_channel *chan, const char *name, const char *pvtid)
 {
 	ast_channel_stage_snapshot(chan);
 	pbx_builtin_setvar_helper(chan, "BRIDGEPEER", name);
@@ -1304,12 +1253,12 @@ static void set_bridge_peer_vars_2party(struct ast_channel *c0, struct ast_chann
 	ast_channel_unlock(c1);
 
 	ast_channel_lock(c0);
-	update_bridge_vars_set(c0, c1_name, c1_pvtid);
+	ast_bridge_vars_set(c0, c1_name, c1_pvtid);
 	UPDATE_BRIDGE_VARS_GET(c0, c0_name, c0_pvtid);
 	ast_channel_unlock(c0);
 
 	ast_channel_lock(c1);
-	update_bridge_vars_set(c1, c0_name, c0_pvtid);
+	ast_bridge_vars_set(c1, c0_name, c0_pvtid);
 	ast_channel_unlock(c1);
 }
 
@@ -1410,7 +1359,7 @@ static void set_bridge_peer_vars_multiparty(struct ast_bridge *bridge)
 		++idx;
 
 		ast_channel_lock(bridge_channel->chan);
-		update_bridge_vars_set(bridge_channel->chan, buf, NULL);
+		ast_bridge_vars_set(bridge_channel->chan, buf, NULL);
 		ast_channel_unlock(bridge_channel->chan);
 	}
 }
@@ -1432,7 +1381,7 @@ static void set_bridge_peer_vars_holding(struct ast_bridge *bridge)
 
 	AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
 		ast_channel_lock(bridge_channel->chan);
-		update_bridge_vars_set(bridge_channel->chan, NULL, NULL);
+		ast_bridge_vars_set(bridge_channel->chan, NULL, NULL);
 		ast_channel_unlock(bridge_channel->chan);
 	}
 }
@@ -1560,6 +1509,7 @@ int ast_bridge_join(struct ast_bridge *bridge,
 		ao2_ref(bridge, -1);
 	}
 	if (!bridge_channel) {
+		ao2_t_cleanup(swap, "Error exit: bridge_channel alloc failed");
 		res = -1;
 		goto join_exit;
 	}
@@ -1567,6 +1517,7 @@ int ast_bridge_join(struct ast_bridge *bridge,
 	ast_assert(features != NULL);
 	if (!features) {
 		ao2_ref(bridge_channel, -1);
+		ao2_t_cleanup(swap, "Error exit: features is NULL");
 		res = -1;
 		goto join_exit;
 	}
@@ -1587,8 +1538,20 @@ int ast_bridge_join(struct ast_bridge *bridge,
 	bridge_channel->features = features;
 	bridge_channel->inhibit_colp = !!(flags & AST_BRIDGE_JOIN_INHIBIT_JOIN_COLP);
 
+	/* allow subclass to peek at upcoming push operation */
+	if (bridge->v_table->push_peek && !res) {
+		struct ast_bridge_channel *bcswap = NULL;
+
+		ast_bridge_lock(bridge);
+		if (bridge_channel->swap) {
+			bcswap = bridge_find_channel(bridge, bridge_channel->swap);
+		}
+		res = bridge->v_table->push_peek(bridge, bridge_channel, bcswap);
+		ast_bridge_unlock(bridge);
+	}
+
 	if (!res) {
-		res = bridge_channel_internal_join(bridge_channel);
+		res = bridge_channel_internal_join(bridge_channel, NULL);
 	}
 
 	/* Cleanup all the data in the bridge channel after it leaves the bridge. */
@@ -1596,6 +1559,8 @@ int ast_bridge_join(struct ast_bridge *bridge,
 	ast_channel_internal_bridge_channel_set(chan, NULL);
 	ast_channel_unlock(chan);
 	bridge_channel->chan = NULL;
+	/* If bridge_channel->swap is not NULL then the join failed. */
+	ao2_t_cleanup(bridge_channel->swap, "Bridge complete: join failed");
 	bridge_channel->swap = NULL;
 	bridge_channel->features = NULL;
 
@@ -1616,15 +1581,21 @@ join_exit:;
 /*! \brief Thread responsible for imparted bridged channels to be departed */
 static void *bridge_channel_depart_thread(void *data)
 {
-	struct ast_bridge_channel *bridge_channel = data;
+	struct bridge_channel_internal_cond *cond = data;
+	struct ast_bridge_channel *bridge_channel = cond->bridge_channel;
 
 	if (bridge_channel->callid) {
 		ast_callid_threadassoc_add(bridge_channel->callid);
 	}
 
-	bridge_channel_internal_join(bridge_channel);
+	bridge_channel_internal_join(bridge_channel, cond);
 
-	/* cleanup */
+	/*
+	 * cleanup
+	 *
+	 * If bridge_channel->swap is not NULL then the join failed.
+	 */
+	ao2_t_cleanup(bridge_channel->swap, "Bridge complete: Departable impart join failed");
 	bridge_channel->swap = NULL;
 	ast_bridge_features_destroy(bridge_channel->features);
 	bridge_channel->features = NULL;
@@ -1638,14 +1609,15 @@ static void *bridge_channel_depart_thread(void *data)
 /*! \brief Thread responsible for independent imparted bridged channels */
 static void *bridge_channel_ind_thread(void *data)
 {
-	struct ast_bridge_channel *bridge_channel = data;
+	struct bridge_channel_internal_cond *cond = data;
+	struct ast_bridge_channel *bridge_channel = cond->bridge_channel;
 	struct ast_channel *chan;
 
 	if (bridge_channel->callid) {
 		ast_callid_threadassoc_add(bridge_channel->callid);
 	}
 
-	bridge_channel_internal_join(bridge_channel);
+	bridge_channel_internal_join(bridge_channel, cond);
 	chan = bridge_channel->chan;
 
 	/* cleanup */
@@ -1653,6 +1625,8 @@ static void *bridge_channel_ind_thread(void *data)
 	ast_channel_internal_bridge_channel_set(chan, NULL);
 	ast_channel_unlock(chan);
 	bridge_channel->chan = NULL;
+	/* If bridge_channel->swap is not NULL then the join failed. */
+	ao2_t_cleanup(bridge_channel->swap, "Bridge complete: Independent impart join failed");
 	bridge_channel->swap = NULL;
 	ast_bridge_features_destroy(bridge_channel->features);
 	bridge_channel->features = NULL;
@@ -1706,22 +1680,48 @@ int ast_bridge_impart(struct ast_bridge *bridge,
 	}
 	ast_channel_unlock(chan);
 	bridge_channel->chan = chan;
-	bridge_channel->swap = swap;
+	bridge_channel->swap = ao2_t_bump(swap, "Setting up bridge impart");
 	bridge_channel->features = features;
 	bridge_channel->inhibit_colp = !!(flags & AST_BRIDGE_IMPART_INHIBIT_JOIN_COLP);
 	bridge_channel->depart_wait =
 		(flags & AST_BRIDGE_IMPART_CHAN_MASK) == AST_BRIDGE_IMPART_CHAN_DEPARTABLE;
 	bridge_channel->callid = ast_read_threadstorage_callid();
 
+	/* allow subclass to peek at swap channel before it can hangup */
+	if (bridge->v_table->push_peek && !res) {
+		struct ast_bridge_channel *bcswap = NULL;
+
+		ast_bridge_lock(bridge);
+		if (bridge_channel->swap) {
+			bcswap = bridge_find_channel(bridge, bridge_channel->swap);
+		}
+		res = bridge->v_table->push_peek(bridge, bridge_channel, bcswap);
+		ast_bridge_unlock(bridge);
+	}
+
 	/* Actually create the thread that will handle the channel */
 	if (!res) {
+		struct bridge_channel_internal_cond cond = {
+			.done = 0,
+			.bridge_channel = bridge_channel
+		};
+		ast_mutex_init(&cond.lock);
+		ast_cond_init(&cond.cond, NULL);
+
 		if ((flags & AST_BRIDGE_IMPART_CHAN_MASK) == AST_BRIDGE_IMPART_CHAN_INDEPENDENT) {
 			res = ast_pthread_create_detached(&bridge_channel->thread, NULL,
-				bridge_channel_ind_thread, bridge_channel);
+				bridge_channel_ind_thread, &cond);
 		} else {
 			res = ast_pthread_create(&bridge_channel->thread, NULL,
-				bridge_channel_depart_thread, bridge_channel);
+				bridge_channel_depart_thread, &cond);
+		}
+
+		if (!res) {
+			bridge_channel_internal_wait(&cond);
 		}
+
+		ast_cond_destroy(&cond.cond);
+		ast_mutex_destroy(&cond.lock);
 	}
 
 	if (res) {
@@ -1730,6 +1730,7 @@ int ast_bridge_impart(struct ast_bridge *bridge,
 		ast_channel_internal_bridge_channel_set(chan, NULL);
 		ast_channel_unlock(chan);
 		bridge_channel->chan = NULL;
+		ao2_t_cleanup(bridge_channel->swap, "Bridge complete: Impart failed");
 		bridge_channel->swap = NULL;
 		ast_bridge_features_destroy(bridge_channel->features);
 		bridge_channel->features = NULL;
@@ -2171,7 +2172,11 @@ int bridge_do_move(struct ast_bridge *dst_bridge, struct ast_bridge_channel *bri
 		/*
 		 * The channel died as a result of being pulled.  Leave it
 		 * pointing to the original bridge.
+		 *
+		 * Clear out the swap channel pointer.  A ref is not held
+		 * by bridge_channel->swap at this point.
 		 */
+		bridge_channel->swap = NULL;
 		bridge_reconfigured(orig_bridge, 0);
 		return -1;
 	}
@@ -3966,6 +3971,15 @@ static enum ast_transfer_result attended_transfer_bridge(struct ast_channel *cha
 		struct ast_channel *chan2, struct ast_bridge *bridge1, struct ast_bridge *bridge2,
 		struct ast_attended_transfer_message *transfer_msg)
 {
+#define BRIDGE_LOCK_ONE_OR_BOTH(b1, b2) \
+	do { \
+		if (b2) { \
+			ast_bridge_lock_both(b1, b2); \
+		} else { \
+			ast_bridge_lock(b1); \
+		} \
+	} while (0)
+
 	static const char *dest = "_attended at transfer/m";
 	struct ast_channel *local_chan;
 	int cause;
@@ -3996,8 +4010,18 @@ static enum ast_transfer_result attended_transfer_bridge(struct ast_channel *cha
 		return AST_BRIDGE_TRANSFER_FAIL;
 	}
 
+	/*
+	 * Since bridges need to be unlocked before entering ast_bridge_impart and
+	 * core_local may call into it then the bridges need to be unlocked here.
+	 */
+	ast_bridge_unlock(bridge1);
+	if (bridge2) {
+		ast_bridge_unlock(bridge2);
+	}
+
 	if (ast_call(local_chan, dest, 0)) {
 		ast_hangup(local_chan);
+		BRIDGE_LOCK_ONE_OR_BOTH(bridge1, bridge2);
 		return AST_BRIDGE_TRANSFER_FAIL;
 	}
 
@@ -4007,8 +4031,10 @@ static enum ast_transfer_result attended_transfer_bridge(struct ast_channel *cha
 		AST_BRIDGE_IMPART_CHAN_INDEPENDENT)) {
 		ast_hangup(local_chan);
 		ao2_cleanup(local_chan);
+		BRIDGE_LOCK_ONE_OR_BOTH(bridge1, bridge2);
 		return AST_BRIDGE_TRANSFER_FAIL;
 	}
+	BRIDGE_LOCK_ONE_OR_BOTH(bridge1, bridge2);
 
 	if (bridge2) {
 		RAII_VAR(struct ast_channel *, local_chan2, NULL, ao2_cleanup);
@@ -4400,6 +4426,7 @@ enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_tra
 	int do_bridge_transfer;
 	enum ast_transfer_result res;
 	const char *app = NULL;
+	int hangup_target = 0;
 
 	to_transferee_bridge = acquire_bridge(to_transferee);
 	to_target_bridge = acquire_bridge(to_transfer_target);
@@ -4479,6 +4506,7 @@ enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_tra
 		ast_bridge_unlock(to_transferee_bridge);
 		ast_bridge_unlock(to_target_bridge);
 
+		hangup_target = 1;
 		goto end;
 	}
 
@@ -4486,6 +4514,12 @@ enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_tra
 	chan_bridged = to_transferee_bridge ? to_transferee : to_transfer_target;
 	chan_unbridged = to_transferee_bridge ? to_transfer_target : to_transferee;
 
+	/*
+	 * Race condition makes it possible for app to be NULL, so get the app prior to
+	 * transferring with a fallback of "unknown".
+	 */
+	app = ast_strdupa(ast_channel_appl(chan_unbridged) ?: "unknown");
+
 	{
 		int chan_count;
 		SCOPED_LOCK(lock, the_bridge, ast_bridge_lock, ast_bridge_unlock);
@@ -4515,6 +4549,11 @@ enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_tra
 	set_transfer_variables_all(to_transferee, channels, 1);
 
 	if (do_bridge_transfer) {
+		/*
+		 * Hang up the target if it was bridged. Note, if it is not bridged
+		 * it is hung up during the masquerade.
+		 */
+		hangup_target = chan_bridged == to_transfer_target;
 		ast_bridge_lock(the_bridge);
 		res = attended_transfer_bridge(chan_bridged, chan_unbridged, the_bridge, NULL, transfer_msg);
 		ast_bridge_unlock(the_bridge);
@@ -4527,7 +4566,6 @@ enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_tra
 		goto end;
 	}
 
-	app = ast_strdupa(ast_channel_appl(chan_unbridged));
 	if (bridge_channel_internal_queue_attended_transfer(transferee, chan_unbridged)) {
 		res = AST_BRIDGE_TRANSFER_FAIL;
 		goto end;
@@ -4539,6 +4577,10 @@ enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_tra
 	res = AST_BRIDGE_TRANSFER_SUCCESS;
 
 end:
+	if (res == AST_BRIDGE_TRANSFER_SUCCESS && hangup_target) {
+		ast_softhangup(to_transfer_target, AST_SOFTHANGUP_DEV);
+	}
+
 	transfer_msg->result = res;
 	ast_bridge_publish_attended_transfer(transfer_msg);
 	return res;
@@ -5198,6 +5240,7 @@ static int manager_bridge_tech_list(struct mansession *s, const struct message *
 	const char *id = astman_get_header(m, "ActionID");
 	RAII_VAR(struct ast_str *, id_text, ast_str_create(128), ast_free);
 	struct ast_bridge_technology *cur;
+	int num_items = 0;
 
 	if (!id_text) {
 		astman_send_error(s, m, "Internal error");
@@ -5208,7 +5251,7 @@ static int manager_bridge_tech_list(struct mansession *s, const struct message *
 		ast_str_set(&id_text, 0, "ActionID: %s\r\n", id);
 	}
 
-	astman_send_ack(s, m, "Bridge technology listing will follow");
+	astman_send_listack(s, m, "Bridge technology listing will follow", "start");
 
 	AST_RWLIST_RDLOCK(&bridge_technologies);
 	AST_RWLIST_TRAVERSE(&bridge_technologies, cur, entry) {
@@ -5226,14 +5269,12 @@ static int manager_bridge_tech_list(struct mansession *s, const struct message *
 			"\r\n",
 			cur->name, type, cur->preference, AST_YESNO(cur->suspended),
 			ast_str_buffer(id_text));
+		++num_items;
 	}
 	AST_RWLIST_UNLOCK(&bridge_technologies);
 
-	astman_append(s,
-		"Event: BridgeTechnologyListComplete\r\n"
-		"%s"
-		"\r\n",
-		ast_str_buffer(id_text));
+	astman_send_list_complete_start(s, m, "BridgeTechnologyListComplete", num_items);
+	astman_send_list_complete_end(s);
 
 	return 0;
 }
@@ -5262,18 +5303,19 @@ static void bridge_prnt_obj(void *v_obj, void *where, ao2_prnt_fn *prnt)
 
 /*!
  * \internal
- * \brief Shutdown the bridging system.
- * \since 12.0.0
+ * \brief Shutdown the bridging system.  Stuff to do on graceful shutdown.
+ * \since 13.3.0
  *
  * \return Nothing
  */
-static void bridge_shutdown(void)
+static void bridge_cleanup(void)
 {
 	ast_manager_unregister("BridgeTechnologyList");
 	ast_manager_unregister("BridgeTechnologySuspend");
 	ast_manager_unregister("BridgeTechnologyUnsuspend");
 	ast_cli_unregister_multiple(bridge_cli, ARRAY_LEN(bridge_cli));
 	ao2_container_unregister("bridges");
+
 	ao2_cleanup(bridges);
 	bridges = NULL;
 	ao2_cleanup(bridge_manager);
@@ -5282,7 +5324,7 @@ static void bridge_shutdown(void)
 
 int ast_bridging_init(void)
 {
-	ast_register_atexit(bridge_shutdown);
+	ast_register_cleanup(bridge_cleanup);
 
 	if (ast_stasis_bridging_init()) {
 		return -1;
diff --git a/main/bridge_after.c b/main/bridge_after.c
index 63b87b5..a41f8a5 100644
--- a/main/bridge_after.c
+++ b/main/bridge_after.c
@@ -32,7 +32,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420940 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/logger.h"
 #include "asterisk/channel.h"
@@ -345,6 +345,7 @@ static void after_bridge_goto_destroy(void *data)
 	ast_free((char *) after_bridge->parseable_goto);
 	ast_free((char *) after_bridge->context);
 	ast_free((char *) after_bridge->exten);
+	ast_free((char *) after_bridge);
 }
 
 /*!
diff --git a/main/bridge_basic.c b/main/bridge_basic.c
index 132988d..3bbd173 100644
--- a/main/bridge_basic.c
+++ b/main/bridge_basic.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428505 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/channel.h"
 #include "asterisk/utils.h"
@@ -45,6 +45,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428505 $")
 #include "asterisk/app.h"
 #include "asterisk/dial.h"
 #include "asterisk/stasis_bridges.h"
+#include "asterisk/stasis_channels.h"
 #include "asterisk/features.h"
 #include "asterisk/format_cache.h"
 #include "asterisk/test.h"
@@ -605,18 +606,17 @@ static int setup_dynamic_feature(void *obj, void *arg, void *data, int flags)
  */
 static int setup_bridge_features_dynamic(struct ast_bridge_features *features, struct ast_channel *chan)
 {
-	RAII_VAR(struct ao2_container *, applicationmap, NULL, ao2_cleanup);
+	struct ao2_container *applicationmap;
 	int res = 0;
 
 	ast_channel_lock(chan);
 	applicationmap = ast_get_chan_applicationmap(chan);
 	ast_channel_unlock(chan);
-	if (!applicationmap) {
-		return 0;
+	if (applicationmap) {
+		ao2_callback_data(applicationmap, 0, setup_dynamic_feature, features, &res);
+		ao2_ref(applicationmap, -1);
 	}
 
-	ao2_callback_data(applicationmap, 0, setup_dynamic_feature, features, &res);
-
 	return res;
 }
 
@@ -1321,7 +1321,10 @@ struct attended_transfer_properties {
 	struct ast_channel *transfer_target;
 	/*! The party that is currently being recalled. Depending on
 	 * the current state, this may be either the party that originally
-	 * was the transferer or the original transfer target
+	 * was the transferer or the original transfer target.  This is
+	 * set with reference when entering the BLOND_NONFINAL, RECALLING,
+	 * and RETRANSFER states, and the reference released on state exit
+	 * if continuing with recall or retransfer to avoid leak.
 	 */
 	struct ast_channel *recall_target;
 	/*! The absolute starting time for running timers */
@@ -1347,6 +1350,8 @@ struct attended_transfer_properties {
 	struct ast_dial *dial;
 	/*! The bridging features the transferer has available */
 	struct ast_flags transferer_features;
+	/*! Saved transferer connected line data for recalling the transferer. */
+	struct ast_party_connected_line original_transferer_colp;
 };
 
 static void attended_transfer_properties_destructor(void *obj)
@@ -1361,6 +1366,7 @@ static void attended_transfer_properties_destructor(void *obj)
 	ast_channel_cleanup(props->transferer);
 	ast_channel_cleanup(props->transfer_target);
 	ast_channel_cleanup(props->recall_target);
+	ast_party_connected_line_free(&props->original_transferer_colp);
 	ast_string_field_free_memory(props);
 	ast_cond_destroy(&props->cond);
 }
@@ -1411,7 +1417,7 @@ static struct attended_transfer_properties *attended_transfer_properties_alloc(
 	char *tech;
 	char *addr;
 	char *serial;
-	RAII_VAR(struct ast_features_xfer_config *, xfer_cfg, NULL, ao2_cleanup);
+	struct ast_features_xfer_config *xfer_cfg;
 	struct ast_flags *transferer_features;
 
 	props = ao2_alloc(sizeof(*props), attended_transfer_properties_destructor);
@@ -1428,6 +1434,7 @@ static struct attended_transfer_properties *attended_transfer_properties_alloc(
 	xfer_cfg = ast_get_chan_features_xfer_config(props->transferer);
 	if (!xfer_cfg) {
 		ast_log(LOG_ERROR, "Unable to get transfer configuration from channel %s\n", ast_channel_name(props->transferer));
+		ast_channel_unlock(props->transferer);
 		ao2_ref(props, -1);
 		return NULL;
 	}
@@ -1442,12 +1449,22 @@ static struct attended_transfer_properties *attended_transfer_properties_alloc(
 	ast_string_field_set(props, context, get_transfer_context(transferer, context));
 	ast_string_field_set(props, failsound, xfer_cfg->xferfailsound);
 	ast_string_field_set(props, xfersound, xfer_cfg->xfersound);
+	ao2_ref(xfer_cfg, -1);
+
+	/*
+	 * Save the transferee's party information for any recall calls.
+	 * This is the only piece of information needed that gets overwritten
+	 * on the transferer channel by the inital call to the transfer target.
+	 */
+	ast_party_connected_line_copy(&props->original_transferer_colp,
+		ast_channel_connected(props->transferer));
 
 	tech = ast_strdupa(ast_channel_name(props->transferer));
 	addr = strchr(tech, '/');
 	if (!addr) {
 		ast_log(LOG_ERROR, "Transferer channel name does not follow typical channel naming format (tech/address)\n");
-		ast_channel_unref(props->transferer);
+		ast_channel_unlock(props->transferer);
+		ao2_ref(props, -1);
 		return NULL;
 	}
 	*addr++ = '\0';
@@ -1678,17 +1695,16 @@ static void publish_transfer_fail(struct attended_transfer_properties *props)
  */
 static void play_sound(struct ast_channel *chan, const char *sound)
 {
-	RAII_VAR(struct ast_bridge_channel *, bridge_channel, NULL, ao2_cleanup);
+	struct ast_bridge_channel *bridge_channel;
 
 	ast_channel_lock(chan);
 	bridge_channel = ast_channel_get_bridge_channel(chan);
 	ast_channel_unlock(chan);
 
-	if (!bridge_channel) {
-		return;
+	if (bridge_channel) {
+		ast_bridge_channel_queue_playfile(bridge_channel, NULL, sound, NULL);
+		ao2_ref(bridge_channel, -1);
 	}
-
-	ast_bridge_channel_queue_playfile(bridge_channel, NULL, sound, NULL);
 }
 
 /*!
@@ -1696,16 +1712,19 @@ static void play_sound(struct ast_channel *chan, const char *sound)
  */
 static void hold(struct ast_channel *chan)
 {
-	RAII_VAR(struct ast_bridge_channel *, bridge_channel, NULL, ao2_cleanup);
+	struct ast_bridge_channel *bridge_channel;
 
-	if (chan) {
-		ast_channel_lock(chan);
-		bridge_channel = ast_channel_get_bridge_channel(chan);
-		ast_channel_unlock(chan);
+	if (!chan) {
+		return;
+	}
 
-		ast_assert(bridge_channel != NULL);
+	ast_channel_lock(chan);
+	bridge_channel = ast_channel_get_bridge_channel(chan);
+	ast_channel_unlock(chan);
 
+	if (bridge_channel) {
 		ast_bridge_channel_write_hold(bridge_channel, NULL);
+		ao2_ref(bridge_channel, -1);
 	}
 }
 
@@ -1714,15 +1733,20 @@ static void hold(struct ast_channel *chan)
  */
 static void unhold(struct ast_channel *chan)
 {
-	RAII_VAR(struct ast_bridge_channel *, bridge_channel, NULL, ao2_cleanup);
+	struct ast_bridge_channel *bridge_channel;
+
+	if (!chan) {
+		return;
+	}
 
 	ast_channel_lock(chan);
 	bridge_channel = ast_channel_get_bridge_channel(chan);
 	ast_channel_unlock(chan);
 
-	ast_assert(bridge_channel != NULL);
-
-	ast_bridge_channel_write_unhold(bridge_channel);
+	if (bridge_channel) {
+		ast_bridge_channel_write_unhold(bridge_channel);
+		ao2_ref(bridge_channel, -1);
+	}
 }
 
 /*!
@@ -1730,15 +1754,16 @@ static void unhold(struct ast_channel *chan)
  */
 static void ringing(struct ast_channel *chan)
 {
-	RAII_VAR(struct ast_bridge_channel *, bridge_channel, NULL, ao2_cleanup);
+	struct ast_bridge_channel *bridge_channel;
 
 	ast_channel_lock(chan);
 	bridge_channel = ast_channel_get_bridge_channel(chan);
 	ast_channel_unlock(chan);
 
-	ast_assert(bridge_channel != NULL);
-
-	ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_RINGING, NULL, 0);
+	if (bridge_channel) {
+		ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_RINGING, NULL, 0);
+		ao2_ref(bridge_channel, -1);
+	}
 }
 
 /*!
@@ -1783,10 +1808,9 @@ static void bridge_unhold(struct ast_bridge *bridge)
 /*!
  * \brief Wrapper for \ref bridge_do_move
  */
-static int bridge_move(struct ast_bridge *dest, struct ast_bridge *src, struct ast_channel *channel, struct ast_channel *swap)
+static void bridge_move(struct ast_bridge *dest, struct ast_bridge *src, struct ast_channel *channel, struct ast_channel *swap)
 {
-	int res;
-	RAII_VAR(struct ast_bridge_channel *, bridge_channel, NULL, ao2_cleanup);
+	struct ast_bridge_channel *bridge_channel;
 
 	ast_bridge_lock_both(src, dest);
 
@@ -1794,18 +1818,18 @@ static int bridge_move(struct ast_bridge *dest, struct ast_bridge *src, struct a
 	bridge_channel = ast_channel_get_bridge_channel(channel);
 	ast_channel_unlock(channel);
 
-	ast_assert(bridge_channel != NULL);
+	if (bridge_channel) {
+		ao2_lock(bridge_channel);
+		bridge_channel->swap = swap;
+		ao2_unlock(bridge_channel);
 
-	ao2_lock(bridge_channel);
-	bridge_channel->swap = swap;
-	ao2_unlock(bridge_channel);
-
-	res = bridge_do_move(dest, bridge_channel, 1, 0);
+		bridge_do_move(dest, bridge_channel, 1, 0);
+	}
 
 	ast_bridge_unlock(dest);
 	ast_bridge_unlock(src);
 
-	return res;
+	ao2_cleanup(bridge_channel);
 }
 
 /*!
@@ -2016,7 +2040,8 @@ static const struct attended_transfer_state_properties state_properties[] = {
 
 static int calling_target_enter(struct attended_transfer_properties *props)
 {
-	return bridge_move(props->target_bridge, props->transferee_bridge, props->transferer, NULL);
+	bridge_move(props->target_bridge, props->transferee_bridge, props->transferer, NULL);
+	return 0;
 }
 
 static enum attended_transfer_state calling_target_exit(struct attended_transfer_properties *props,
@@ -2055,10 +2080,7 @@ static enum attended_transfer_state calling_target_exit(struct attended_transfer
 
 static int hesitant_enter(struct attended_transfer_properties *props)
 {
-	if (bridge_move(props->transferee_bridge, props->target_bridge, props->transferer, NULL)) {
-		return -1;
-	}
-
+	bridge_move(props->transferee_bridge, props->target_bridge, props->transferer, NULL);
 	unhold(props->transferer);
 	return 0;
 }
@@ -2098,11 +2120,7 @@ static enum attended_transfer_state hesitant_exit(struct attended_transfer_prope
 
 static int rebridge_enter(struct attended_transfer_properties *props)
 {
-	if (bridge_move(props->transferee_bridge, props->target_bridge,
-			props->transferer, NULL)) {
-		return -1;
-	}
-
+	bridge_move(props->transferee_bridge, props->target_bridge, props->transferer, NULL);
 	unhold(props->transferer);
 	return 0;
 }
@@ -2261,6 +2279,7 @@ static int blond_nonfinal_enter(struct attended_transfer_properties *props)
 {
 	int res;
 	props->superstate = SUPERSTATE_RECALL;
+	/* move the transfer target to the recall target along with its reference */
 	props->recall_target = ast_channel_ref(props->transfer_target);
 	res = blond_enter(props);
 	props->transfer_target = ast_channel_unref(props->transfer_target);
@@ -2277,8 +2296,8 @@ static enum attended_transfer_state blond_nonfinal_exit(struct attended_transfer
 		return TRANSFER_RESUME;
 	case STIMULUS_TIMEOUT:
 		ast_softhangup(props->recall_target, AST_SOFTHANGUP_EXPLICIT);
-		props->recall_target = ast_channel_unref(props->recall_target);
 	case STIMULUS_RECALL_TARGET_HANGUP:
+		props->recall_target = ast_channel_unref(props->recall_target);
 		return TRANSFER_RECALLING;
 	case STIMULUS_NONE:
 	case STIMULUS_DTMF_ATXFER_ABORT:
@@ -2330,10 +2349,50 @@ static void recall_callback(struct ast_dial *dial)
 	}
 }
 
+/*!
+ * \internal
+ * \brief Setup common things to transferrer and transfer_target recall channels.
+ *
+ * \param recall Channel for recalling a party.
+ * \param transferer Channel supplying recall information.
+ *
+ * \details
+ * Setup callid, variables, datastores, accountcode, and peeraccount.
+ *
+ * \pre Both channels are locked on entry.
+ *
+ * \pre COLP and CLID on the recall channel are setup by the caller but not
+ * explicitly published yet.
+ *
+ * \return Nothing
+ */
+static void common_recall_channel_setup(struct ast_channel *recall, struct ast_channel *transferer)
+{
+	struct ast_callid *callid;
+
+	callid = ast_read_threadstorage_callid();
+	if (callid) {
+		ast_channel_callid_set(recall, callid);
+		ast_callid_unref(callid);
+	}
+
+	ast_channel_inherit_variables(transferer, recall);
+	ast_channel_datastore_inherit(transferer, recall);
+
+	/*
+	 * Stage a snapshot to ensure that a snapshot is always done
+	 * on the recall channel so earler COLP and CLID setup will
+	 * get published.
+	 */
+	ast_channel_stage_snapshot(recall);
+	ast_channel_req_accountcodes(recall, transferer, AST_CHANNEL_REQUESTOR_REPLACEMENT);
+	ast_channel_stage_snapshot_done(recall);
+}
 
 static int recalling_enter(struct attended_transfer_properties *props)
 {
 	RAII_VAR(struct ast_format_cap *, cap, ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT), ao2_cleanup);
+	struct ast_channel *recall;
 
 	if (!cap) {
 		return -1;
@@ -2359,7 +2418,26 @@ static int recalling_enter(struct attended_transfer_properties *props)
 		return -1;
 	}
 
-	ast_dial_set_state_callback(props->dial, &recall_callback);
+	/*
+	 * Setup callid, variables, datastores, accountcode, peeraccount,
+	 * COLP, and CLID on the recalled transferrer.
+	 */
+	recall = ast_dial_get_channel(props->dial, 0);
+	if (!recall) {
+		return -1;
+	}
+	ast_channel_lock_both(recall, props->transferer);
+
+	ast_party_caller_copy(ast_channel_caller(recall),
+		ast_channel_caller(props->transferer));
+	ast_party_connected_line_copy(ast_channel_connected(recall),
+		&props->original_transferer_colp);
+
+	common_recall_channel_setup(recall, props->transferer);
+	ast_channel_unlock(recall);
+	ast_channel_unlock(props->transferer);
+
+	ast_dial_set_state_callback(props->dial, recall_callback);
 
 	ao2_ref(props, +1);
 	ast_dial_set_user_data(props->dial, props);
@@ -2406,6 +2484,7 @@ static enum attended_transfer_state recalling_exit(struct attended_transfer_prop
 		if (ast_bridge_impart(props->transferee_bridge, props->recall_target, NULL, NULL,
 			AST_BRIDGE_IMPART_CHAN_INDEPENDENT)) {
 			ast_hangup(props->recall_target);
+			ast_channel_unref(props->recall_target);
 			return TRANSFER_FAIL;
 		}
 		return TRANSFER_RESUME;
@@ -2486,6 +2565,20 @@ static int retransfer_enter(struct attended_transfer_properties *props)
 		return -1;
 	}
 
+	/*
+	 * Setup callid, variables, datastores, accountcode, peeraccount,
+	 * and COLP on the recalled transfer target.
+	 */
+	ast_channel_lock_both(props->recall_target, props->transferer);
+
+	ast_party_connected_line_copy(ast_channel_connected(props->recall_target),
+		&props->original_transferer_colp);
+	ast_party_id_reset(&ast_channel_connected(props->recall_target)->priv);
+
+	common_recall_channel_setup(props->recall_target, props->recall_target);
+	ast_channel_unlock(props->recall_target);
+	ast_channel_unlock(props->transferer);
+
 	if (ast_call(props->recall_target, destination, 0)) {
 		ast_log(LOG_ERROR, "Unable to place outbound call to recall target\n");
 		ast_hangup(props->recall_target);
@@ -2498,6 +2591,7 @@ static int retransfer_enter(struct attended_transfer_properties *props)
 		AST_BRIDGE_IMPART_CHAN_INDEPENDENT)) {
 		ast_log(LOG_ERROR, "Unable to place recall target into bridge\n");
 		ast_hangup(props->recall_target);
+		ast_channel_unref(props->recall_target);
 		return -1;
 	}
 
@@ -2747,7 +2841,8 @@ static void transfer_pull(struct ast_bridge *self, struct ast_bridge_channel *br
 	}
 
 	if (self->num_channels == 1) {
-		RAII_VAR(struct ast_bridge_channel *, transferer_bridge_channel, NULL, ao2_cleanup);
+		struct ast_bridge_channel *transferer_bridge_channel;
+		int not_transferer;
 
 		ast_channel_lock(props->transferer);
 		transferer_bridge_channel = ast_channel_get_bridge_channel(props->transferer);
@@ -2757,7 +2852,9 @@ static void transfer_pull(struct ast_bridge *self, struct ast_bridge_channel *br
 			return;
 		}
 
-		if (AST_LIST_FIRST(&self->channels) != transferer_bridge_channel) {
+		not_transferer = AST_LIST_FIRST(&self->channels) != transferer_bridge_channel;
+		ao2_ref(transferer_bridge_channel, -1);
+		if (not_transferer) {
 			return;
 		}
 	}
@@ -2794,7 +2891,8 @@ static void recall_pull(struct ast_bridge *self, struct ast_bridge_channel *brid
 	}
 
 	if (self->num_channels == 1) {
-		RAII_VAR(struct ast_bridge_channel *, target_bridge_channel, NULL, ao2_cleanup);
+		struct ast_bridge_channel *target_bridge_channel;
+
 		if (!props->recall_target) {
 			/* No recall target means that the pull happened on a transferee. If there's still
 			 * a channel left in the bridge, we don't need to send a stimulus
@@ -2806,12 +2904,11 @@ static void recall_pull(struct ast_bridge *self, struct ast_bridge_channel *brid
 		target_bridge_channel = ast_channel_get_bridge_channel(props->recall_target);
 		ast_channel_unlock(props->recall_target);
 
-		if (!target_bridge_channel) {
-			return;
-		}
-
-		if (AST_LIST_FIRST(&self->channels) == target_bridge_channel) {
-			stimulate_attended_transfer(props, STIMULUS_TRANSFEREE_HANGUP);
+		if (target_bridge_channel) {
+			if (AST_LIST_FIRST(&self->channels) == target_bridge_channel) {
+				stimulate_attended_transfer(props, STIMULUS_TRANSFEREE_HANGUP);
+			}
+			ao2_ref(target_bridge_channel, -1);
 		}
 	}
 }
@@ -2833,7 +2930,8 @@ static void bridge_personality_atxfer_pull(struct ast_bridge *self, struct ast_b
 
 static enum attended_transfer_stimulus wait_for_stimulus(struct attended_transfer_properties *props)
 {
-	RAII_VAR(struct stimulus_list *, list, NULL, ast_free_ptr);
+	enum attended_transfer_stimulus stimulus;
+	struct stimulus_list *list;
 	SCOPED_MUTEX(lock, ao2_object_get_lockaddr(props));
 
 	while (!(list = AST_LIST_REMOVE_HEAD(&props->stimulus_queue, next))) {
@@ -2864,7 +2962,9 @@ static enum attended_transfer_stimulus wait_for_stimulus(struct attended_transfe
 			}
 		}
 	}
-	return list->stimulus;
+	stimulus = list->stimulus;
+	ast_free(list);
+	return stimulus;
 }
 
 /*!
@@ -2880,6 +2980,18 @@ static enum attended_transfer_stimulus wait_for_stimulus(struct attended_transfe
 static void *attended_transfer_monitor_thread(void *data)
 {
 	struct attended_transfer_properties *props = data;
+	struct ast_callid *callid;
+
+	/*
+	 * Set thread callid to the transferer's callid because we
+	 * are doing all this on that channel's behalf.
+	 */
+	ast_channel_lock(props->transferer);
+	callid = ast_channel_callid(props->transferer);
+	ast_channel_unlock(props->transferer);
+	if (callid) {
+		ast_callid_threadassoc_add(callid);
+	}
 
 	for (;;) {
 		enum attended_transfer_stimulus stimulus;
@@ -2912,6 +3024,11 @@ static void *attended_transfer_monitor_thread(void *data)
 
 	attended_transfer_properties_shutdown(props);
 
+	if (callid) {
+		ast_callid_unref(callid);
+		ast_callid_threadassoc_remove();
+	}
+
 	return NULL;
 }
 
@@ -2942,7 +3059,7 @@ static int add_transferer_role(struct ast_channel *chan, struct ast_bridge_featu
 	const char *atxfer_threeway;
 	const char *atxfer_complete;
 	const char *atxfer_swap;
-	RAII_VAR(struct ast_features_xfer_config *, xfer_cfg, NULL, ao2_cleanup);
+	struct ast_features_xfer_config *xfer_cfg;
 	SCOPED_CHANNELLOCK(lock, chan);
 
 	xfer_cfg = ast_get_chan_features_xfer_config(chan);
@@ -2960,6 +3077,7 @@ static int add_transferer_role(struct ast_channel *chan, struct ast_bridge_featu
 		atxfer_complete = ast_strdupa(xfer_cfg->atxfercomplete);
 		atxfer_swap = ast_strdupa(xfer_cfg->atxferswap);
 	}
+	ao2_ref(xfer_cfg, -1);
 
 	return ast_channel_add_bridge_role(chan, AST_TRANSFERER_ROLE_NAME) ||
 		ast_channel_set_bridge_role_option(chan, AST_TRANSFERER_ROLE_NAME, "abort", atxfer_abort) ||
@@ -2980,7 +3098,7 @@ static int grab_transfer(struct ast_channel *chan, char *exten, size_t exten_len
 	int digit_timeout;
 	int attempts = 0;
 	int max_attempts;
-	RAII_VAR(struct ast_features_xfer_config *, xfer_cfg, NULL, ao2_cleanup);
+	struct ast_features_xfer_config *xfer_cfg;
 	char *retry_sound;
 	char *invalid_sound;
 
@@ -2995,6 +3113,7 @@ static int grab_transfer(struct ast_channel *chan, char *exten, size_t exten_len
 	max_attempts = xfer_cfg->transferdialattempts;
 	retry_sound = ast_strdupa(xfer_cfg->transferretrysound);
 	invalid_sound = ast_strdupa(xfer_cfg->transferinvalidsound);
+	ao2_ref(xfer_cfg, -1);
 	ast_channel_unlock(chan);
 
 	/* Play the simple "transfer" prompt out and wait */
diff --git a/main/bridge_channel.c b/main/bridge_channel.c
index 232141c..3874e50 100644
--- a/main/bridge_channel.c
+++ b/main/bridge_channel.c
@@ -32,10 +32,9 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428602 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <signal.h>
-#include <semaphore.h>
 
 #include "asterisk/heap.h"
 #include "asterisk/astobj2.h"
@@ -56,6 +55,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428602 $")
 #include "asterisk/parking.h"
 #include "asterisk/causes.h"
 #include "asterisk/test.h"
+#include "asterisk/sem.h"
 
 /*!
  * \brief Used to queue an action frame onto a bridge channel and write an action frame into a bridge.
@@ -101,7 +101,7 @@ struct bridge_sync {
 	/*! Unique ID of this synchronization object. Corresponds with ID in synchronous frame payload */
 	unsigned int id;
 	/*! Semaphore used for synchronization */
-	sem_t sem;
+	struct ast_sem sem;
 	/*! Pointer to next entry in the list */
 	AST_LIST_ENTRY(bridge_sync) list;
 };
@@ -124,7 +124,7 @@ static void bridge_sync_init(struct bridge_sync *sync_struct, unsigned int id)
 {
 	memset(sync_struct, 0, sizeof(*sync_struct));
 	sync_struct->id = id;
-	sem_init(&sync_struct->sem, 0, 0);
+	ast_sem_init(&sync_struct->sem, 0, 0);
 
 	AST_RWLIST_WRLOCK(&sync_structs);
 	AST_RWLIST_INSERT_TAIL(&sync_structs, sync_struct, list);
@@ -157,7 +157,7 @@ static void bridge_sync_cleanup(struct bridge_sync *sync_struct)
 	AST_LIST_TRAVERSE_SAFE_END;
 	AST_RWLIST_UNLOCK(&sync_structs);
 
-	sem_destroy(&sync_struct->sem);
+	ast_sem_destroy(&sync_struct->sem);
 }
 
 /*!
@@ -189,7 +189,7 @@ static void bridge_sync_wait(struct bridge_sync *sync_struct)
 		.tv_nsec = timeout_val.tv_usec * 1000,
 	};
 
-	sem_timedwait(&sync_struct->sem, &timeout_spec);
+	ast_sem_timedwait(&sync_struct->sem, &timeout_spec);
 }
 
 /*!
@@ -204,7 +204,7 @@ static void bridge_sync_wait(struct bridge_sync *sync_struct)
  */
 static void bridge_sync_signal(struct bridge_sync *sync_struct)
 {
-	sem_post(&sync_struct->sem);
+	ast_sem_post(&sync_struct->sem);
 }
 
 void ast_bridge_channel_lock_bridge(struct ast_bridge_channel *bridge_channel)
@@ -289,6 +289,10 @@ void ast_bridge_channel_leave_bridge_nolock(struct ast_bridge_channel *bridge_ch
 
 	channel_set_cause(bridge_channel->chan, cause);
 
+	ast_channel_lock(bridge_channel->chan);
+	ast_bridge_vars_set(bridge_channel->chan, NULL, NULL);
+	ast_channel_unlock(bridge_channel->chan);
+
 	/* Change the state on the bridge channel */
 	bridge_channel->state = new_state;
 
@@ -979,7 +983,8 @@ int ast_bridge_channel_write_control_data(struct ast_bridge_channel *bridge_chan
 
 int ast_bridge_channel_write_hold(struct ast_bridge_channel *bridge_channel, const char *moh_class)
 {
-	RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
+	struct ast_json *blob;
+	int res;
 	size_t datalen;
 
 	if (!ast_strlen_zero(moh_class)) {
@@ -990,12 +995,16 @@ int ast_bridge_channel_write_hold(struct ast_bridge_channel *bridge_channel, con
 	} else {
 		moh_class = NULL;
 		datalen = 0;
+		blob = NULL;
 	}
 
 	ast_channel_publish_cached_blob(bridge_channel->chan, ast_channel_hold_type(), blob);
 
-	return ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_HOLD,
+	res = ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_HOLD,
 		moh_class, datalen);
+
+	ast_json_unref(blob);
+	return res;
 }
 
 int ast_bridge_channel_write_unhold(struct ast_bridge_channel *bridge_channel)
@@ -1510,6 +1519,51 @@ static void testsuite_notify_feature_success(struct ast_channel *chan, const cha
 #endif /* TEST_FRAMEWORK */
 }
 
+static int bridge_channel_feature_digit_add(
+	struct ast_bridge_channel *bridge_channel, int digit, size_t dtmf_len)
+{
+	if (dtmf_len < ARRAY_LEN(bridge_channel->dtmf_hook_state.collected) - 1) {
+		/* Add the new digit to the DTMF string so we can do our matching */
+		bridge_channel->dtmf_hook_state.collected[dtmf_len++] = digit;
+		bridge_channel->dtmf_hook_state.collected[dtmf_len] = '\0';
+
+		ast_debug(1, "DTMF feature string on %p(%s) is now '%s'\n",
+			  bridge_channel, ast_channel_name(bridge_channel->chan),
+			  bridge_channel->dtmf_hook_state.collected);
+	}
+
+	return dtmf_len;
+}
+
+static unsigned int bridge_channel_feature_digit_timeout(struct ast_bridge_channel *bridge_channel)
+{
+	unsigned int digit_timeout;
+	struct ast_features_general_config *gen_cfg;
+
+	/* Determine interdigit timeout */
+	ast_channel_lock(bridge_channel->chan);
+	gen_cfg = ast_get_chan_features_general_config(bridge_channel->chan);
+	ast_channel_unlock(bridge_channel->chan);
+
+	if (!gen_cfg) {
+		ast_log(LOG_ERROR, "Unable to retrieve features configuration.\n");
+		return 3000; /* Pick a reasonable failsafe timeout in ms */
+	}
+
+	digit_timeout = gen_cfg->featuredigittimeout;
+	ao2_ref(gen_cfg, -1);
+
+	return digit_timeout;
+}
+
+void ast_bridge_channel_feature_digit_add(struct ast_bridge_channel *bridge_channel, int digit)
+{
+	if (digit) {
+		bridge_channel_feature_digit_add(
+			bridge_channel, digit, strlen(bridge_channel->dtmf_hook_state.collected));
+	}
+}
+
 void ast_bridge_channel_feature_digit(struct ast_bridge_channel *bridge_channel, int digit)
 {
 	struct ast_bridge_features *features = bridge_channel->features;
@@ -1527,17 +1581,10 @@ void ast_bridge_channel_feature_digit(struct ast_bridge_channel *bridge_channel,
 	}
 
 	if (digit) {
-		/* There should always be room for the new digit. */
-		ast_assert(dtmf_len < ARRAY_LEN(bridge_channel->dtmf_hook_state.collected) - 1);
-
-		/* Add the new digit to the DTMF string so we can do our matching */
-		bridge_channel->dtmf_hook_state.collected[dtmf_len++] = digit;
-		bridge_channel->dtmf_hook_state.collected[dtmf_len] = '\0';
-
-		ast_debug(1, "DTMF feature string on %p(%s) is now '%s'\n",
-			bridge_channel, ast_channel_name(bridge_channel->chan),
-			bridge_channel->dtmf_hook_state.collected);
+		dtmf_len = bridge_channel_feature_digit_add(bridge_channel, digit, dtmf_len);
+	}
 
+	while (digit) {
 		/* See if a DTMF feature hook matches or can match */
 		hook = ao2_find(features->dtmf_hooks, bridge_channel->dtmf_hook_state.collected,
 			OBJ_SEARCH_PARTIAL_KEY);
@@ -1545,25 +1592,12 @@ void ast_bridge_channel_feature_digit(struct ast_bridge_channel *bridge_channel,
 			ast_debug(1, "No DTMF feature hooks on %p(%s) match '%s'\n",
 				bridge_channel, ast_channel_name(bridge_channel->chan),
 				bridge_channel->dtmf_hook_state.collected);
+			break;
 		} else if (dtmf_len != strlen(hook->dtmf.code)) {
 			unsigned int digit_timeout;
-			struct ast_features_general_config *gen_cfg;
-
 			/* Need more digits to match */
 			ao2_ref(hook, -1);
-
-			/* Determine interdigit timeout */
-			ast_channel_lock(bridge_channel->chan);
-			gen_cfg = ast_get_chan_features_general_config(bridge_channel->chan);
-			ast_channel_unlock(bridge_channel->chan);
-			if (!gen_cfg) {
-				ast_log(LOG_ERROR, "Unable to retrieve features configuration.\n");
-				digit_timeout = 3000; /* Pick a reasonable failsafe timeout in ms */
-			} else {
-				digit_timeout = gen_cfg->featuredigittimeout;
-				ao2_ref(gen_cfg, -1);
-			}
-
+			digit_timeout = bridge_channel_feature_digit_timeout(bridge_channel);
 			bridge_channel->dtmf_hook_state.interdigit_timeout =
 				ast_tvadd(ast_tvnow(), ast_samp2tv(digit_timeout, 1000));
 			return;
@@ -1612,10 +1646,21 @@ void ast_bridge_channel_feature_digit(struct ast_bridge_channel *bridge_channel,
 			 */
 			if (bridge_channel->chan && ast_check_hangup_locked(bridge_channel->chan)) {
 				ast_bridge_channel_kick(bridge_channel, 0);
+				bridge_channel->dtmf_hook_state.collected[0] = '\0';
+				return;
+			}
+
+			/* if there is dtmf that has been collected then loop back through,
+			   but set digit to -1 so it doesn't try to do an add since the dtmf
+			   is already in the buffer */
+			dtmf_len = strlen(bridge_channel->dtmf_hook_state.collected);
+			if (!dtmf_len) {
+				return;
 			}
-			return;
 		}
-	} else {
+	}
+
+	if (!digit) {
 		ast_debug(1, "DTMF feature string collection on %p(%s) timed out\n",
 			bridge_channel, ast_channel_name(bridge_channel->chan));
 	}
@@ -1720,6 +1765,17 @@ static void after_bridge_move_channel(struct ast_channel *chan_bridged, void *da
 		return;
 	}
 
+	/* The ast_channel_move function will end up updating the connected line information
+	 * on chan_target to the value we have here, but will not inform it. To ensure that
+	 * AST_FRAME_READ_ACTION_CONNECTED_LINE_MACRO is executed we wipe it away here. If
+	 * we don't do this then the change will be considered redundant, since the connected
+	 * line information is already there (despite the channel not being told).
+	 */
+	ast_channel_lock(chan_target);
+	ast_party_connected_line_free(ast_channel_connected_indicated(chan_target));
+	ast_party_connected_line_init(ast_channel_connected_indicated(chan_target));
+	ast_channel_unlock(chan_target);
+
 	if ((payload_size = ast_connected_line_build_data(connected_line_data,
 		sizeof(connected_line_data), &connected_target, NULL)) != -1) {
 		struct ast_control_read_action_payload *frame_payload;
@@ -1733,6 +1789,15 @@ static void after_bridge_move_channel(struct ast_channel *chan_bridged, void *da
 		ast_queue_control_data(chan_target, AST_CONTROL_READ_ACTION, frame_payload, frame_size);
 	}
 
+	/* A connected line update is queued so that if chan_target is remotely involved with
+	 * anything (such as dialing a channel) the other channel(s) will be informed of the
+	 * new channel they are involved with.
+	 */
+	ast_channel_lock(chan_target);
+	ast_connected_line_copy_from_caller(&connected_target, ast_channel_caller(chan_target));
+	ast_channel_queue_connected_line_update(chan_target, &connected_target, NULL);
+	ast_channel_unlock(chan_target);
+
 	ast_party_connected_line_free(&connected_target);
 }
 
@@ -1853,6 +1918,13 @@ static void bridge_channel_handle_action(struct ast_bridge_channel *bridge_chann
 	default:
 		break;
 	}
+
+	/* While invoking an action it is possible for the channel to be hung up. So
+	 * that the bridge respects this we check here and if hung up kick it out.
+	 */
+	if (bridge_channel->chan && ast_check_hangup_locked(bridge_channel->chan)) {
+		ast_bridge_channel_kick(bridge_channel, 0);
+	}
 }
 
 /*!
@@ -1994,6 +2066,19 @@ int bridge_channel_internal_push(struct ast_bridge_channel *bridge_channel)
 			bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan));
 		return -1;
 	}
+
+	if (swap) {
+		int dissolve = ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_DISSOLVE_EMPTY);
+
+		/* This flag is cleared so the act of this channel leaving does not cause it to dissolve if need be */
+		ast_clear_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_DISSOLVE_EMPTY);
+
+		ast_bridge_channel_leave_bridge(swap, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, 0);
+		bridge_channel_internal_pull(swap);
+
+		ast_set2_flag(&bridge->feature_flags, dissolve, AST_BRIDGE_FLAG_DISSOLVE_EMPTY);
+	}
+
 	bridge_channel->in_bridge = 1;
 	bridge_channel->just_joined = 1;
 	AST_LIST_INSERT_TAIL(&bridge->channels, bridge_channel, entry);
@@ -2015,10 +2100,6 @@ int bridge_channel_internal_push(struct ast_bridge_channel *bridge_channel)
 		bridge->uniqueid);
 
 	ast_bridge_publish_enter(bridge, bridge_channel->chan, swap ? swap->chan : NULL);
-	if (swap) {
-		ast_bridge_channel_leave_bridge(swap, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, 0);
-		bridge_channel_internal_pull(swap);
-	}
 
 	/* Clear any BLINDTRANSFER and ATTENDEDTRANSFER since the transfer has completed. */
 	pbx_builtin_setvar_helper(bridge_channel->chan, "BLINDTRANSFER", NULL);
@@ -2490,11 +2571,31 @@ static void bridge_channel_event_join_leave(struct ast_bridge_channel *bridge_ch
 	ao2_iterator_destroy(&iter);
 }
 
-/*! \brief Join a channel to a bridge and handle anything the bridge may want us to do */
-int bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel)
+void bridge_channel_internal_wait(struct bridge_channel_internal_cond *cond)
+{
+	ast_mutex_lock(&cond->lock);
+	while (!cond->done) {
+		ast_cond_wait(&cond->cond, &cond->lock);
+	}
+	ast_mutex_unlock(&cond->lock);
+}
+
+void bridge_channel_internal_signal(struct bridge_channel_internal_cond *cond)
+{
+	if (cond) {
+		ast_mutex_lock(&cond->lock);
+		cond->done = 1;
+		ast_cond_signal(&cond->cond);
+		ast_mutex_unlock(&cond->lock);
+	}
+}
+
+int bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel,
+				 struct bridge_channel_internal_cond *cond)
 {
 	int res = 0;
 	struct ast_bridge_features *channel_features;
+	struct ast_channel *swap;
 
 	ast_debug(1, "Bridge %s: %p(%s) is joining\n",
 		bridge_channel->bridge->uniqueid,
@@ -2520,6 +2621,7 @@ int bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel)
 			bridge_channel->bridge->uniqueid,
 			bridge_channel,
 			ast_channel_name(bridge_channel->chan));
+		bridge_channel_internal_signal(cond);
 		return -1;
 	}
 	ast_channel_internal_bridge_set(bridge_channel->chan, bridge_channel->bridge);
@@ -2538,6 +2640,9 @@ int bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel)
 		bridge_channel->bridge->callid = ast_read_threadstorage_callid();
 	}
 
+	/* Take the swap channel ref from the bridge_channel struct. */
+	swap = bridge_channel->swap;
+
 	if (bridge_channel_internal_push(bridge_channel)) {
 		int cause = bridge_channel->bridge->cause;
 
@@ -2551,6 +2656,8 @@ int bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel)
 	}
 	bridge_reconfigured(bridge_channel->bridge, !bridge_channel->inhibit_colp);
 
+	bridge_channel_internal_signal(cond);
+
 	if (bridge_channel->state == BRIDGE_CHANNEL_STATE_WAIT) {
 		/*
 		 * Indicate a source change since this channel is entering the
@@ -2563,6 +2670,11 @@ int bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel)
 		}
 
 		ast_bridge_unlock(bridge_channel->bridge);
+
+		/* Must release any swap ref after unlocking the bridge. */
+		ao2_t_cleanup(swap, "Bridge push with swap successful");
+		swap = NULL;
+
 		bridge_channel_event_join_leave(bridge_channel, AST_BRIDGE_HOOK_TYPE_JOIN);
 
 		while (bridge_channel->state == BRIDGE_CHANNEL_STATE_WAIT) {
@@ -2583,6 +2695,9 @@ int bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel)
 
 	ast_bridge_unlock(bridge_channel->bridge);
 
+	/* Must release any swap ref after unlocking the bridge. */
+	ao2_t_cleanup(swap, "Bridge push with swap failed or exited immediately");
+
 	/* Complete any active hold before exiting the bridge. */
 	if (ast_channel_hold_state(bridge_channel->chan) == AST_CONTROL_HOLD) {
 		ast_debug(1, "Channel %s simulating UNHOLD for bridge end.\n",
diff --git a/main/bridge_roles.c b/main/bridge_roles.c
index af61dc5..1d781ca 100644
--- a/main/bridge_roles.c
+++ b/main/bridge_roles.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 396182 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <signal.h>
 
diff --git a/main/bucket.c b/main/bucket.c
index 8fb4e65..7618761 100644
--- a/main/bucket.c
+++ b/main/bucket.c
@@ -60,7 +60,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 413589 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #ifdef HAVE_URIPARSER
 #include <uriparser/Uri.h>
@@ -282,7 +282,7 @@ int __ast_bucket_scheme_register(const char *name, struct ast_sorcery_wizard *bu
 
 	ast_verb(2, "Registered bucket scheme '%s'\n", name);
 
-	ast_module_ref(module);
+	ast_module_shutdown_ref(module);
 
 	return 0;
 }
@@ -882,9 +882,8 @@ static int bucket_scheme_cmp(void *obj, void *arg, int flags)
 /*! \brief Cleanup function for graceful shutdowns */
 static void bucket_cleanup(void)
 {
-	if (bucket_sorcery) {
-		ast_sorcery_unref(bucket_sorcery);
-	}
+	ast_sorcery_unref(bucket_sorcery);
+	bucket_sorcery = NULL;
 
 	ast_sorcery_wizard_unregister(&bucket_wizard);
 	ast_sorcery_wizard_unregister(&bucket_file_wizard);
diff --git a/main/callerid.c b/main/callerid.c
index 2e96c35..a99dafc 100644
--- a/main/callerid.c
+++ b/main/callerid.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 425155 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <time.h>
 #include <math.h>
diff --git a/main/ccss.c b/main/ccss.c
index 05e8b16..0605810 100644
--- a/main/ccss.c
+++ b/main/ccss.c
@@ -36,7 +36,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420124 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/astobj2.h"
 #include "asterisk/strings.h"
@@ -825,7 +825,7 @@ int ast_cc_set_param(struct ast_cc_config_params *params, const char * const nam
 		return 0;
 	}
 
-	if (!sscanf(value, "%30u", &value_as_uint) == 1) {
+	if (sscanf(value, "%30u", &value_as_uint) != 1) {
 		return -1;
 	}
 
@@ -2237,9 +2237,7 @@ static void call_destructor_with_no_monitor(const char * const monitor_type, voi
  * Note that it is not necessarily erroneous to add the same
  * device to the tree twice. If the same device is called by
  * two different extension during the same call, then
- * that is a legitimate situation. Of course, I'm pretty sure
- * the dialed_interfaces global datastore will not allow that
- * to happen anyway.
+ * that is a legitimate situation.
  *
  * \param device_name The name of the device being added to the tree
  * \param dialstring The dialstring used to dial the device being added
@@ -4688,7 +4686,7 @@ int ast_cc_init(void)
 	initialize_cc_devstate_map();
 	res |= ast_devstate_prov_add("ccss", ccss_device_state);
 
-	ast_register_atexit(cc_shutdown);
+	ast_register_cleanup(cc_shutdown);
 
 	return res;
 }
diff --git a/main/cdr.c b/main/cdr.c
index 559acde..07c0466 100644
--- a/main/cdr.c
+++ b/main/cdr.c
@@ -45,7 +45,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 427902 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <signal.h>
 #include <inttypes.h>
@@ -912,6 +912,16 @@ static struct cdr_object *cdr_object_create_and_append(struct cdr_object *cdr)
 	ast_string_field_set(new_cdr, linkedid, cdr_last->linkedid);
 	ast_string_field_set(new_cdr, appl, cdr_last->appl);
 	ast_string_field_set(new_cdr, data, cdr_last->data);
+	ast_string_field_set(new_cdr, context, cdr_last->context);
+	ast_string_field_set(new_cdr, exten, cdr_last->exten);
+
+	/*
+	 * If the current CDR says to disable all future ones,
+	 * keep the disable chain going
+	 */
+	if (ast_test_flag(&cdr_last->flags, AST_CDR_FLAG_DISABLE_ALL)) {
+		ast_set_flag(&new_cdr->flags, AST_CDR_FLAG_DISABLE_ALL);
+	}
 
 	/* Copy over other Party A information */
 	cdr_object_snapshot_copy(&new_cdr->party_a, &cdr_last->party_a);
@@ -1257,11 +1267,11 @@ static void cdr_object_finalize(struct cdr_object *cdr)
 	/* tv_usec is suseconds_t, which could be int or long */
 	ast_debug(1, "Finalized CDR for %s - start %ld.%06ld answer %ld.%06ld end %ld.%06ld dispo %s\n",
 			cdr->party_a.snapshot->name,
-			cdr->start.tv_sec,
+			(long)cdr->start.tv_sec,
 			(long)cdr->start.tv_usec,
-			cdr->answer.tv_sec,
+			(long)cdr->answer.tv_sec,
 			(long)cdr->answer.tv_usec,
-			cdr->end.tv_sec,
+			(long)cdr->end.tv_sec,
 			(long)cdr->end.tv_usec,
 			ast_cdr_disp2str(cdr->disposition));
 }
@@ -1296,7 +1306,7 @@ static void cdr_object_check_party_a_answer(struct cdr_object *cdr) {
 		cdr->answer = ast_tvnow();
 		/* tv_usec is suseconds_t, which could be int or long */
 		CDR_DEBUG(mod_cfg, "%p - Set answered time to %ld.%06ld\n", cdr,
-			cdr->answer.tv_sec,
+			(long)cdr->answer.tv_sec,
 			(long)cdr->answer.tv_usec);
 	}
 }
@@ -3096,13 +3106,9 @@ int ast_cdr_serialize_variables(const char *channel_name, struct ast_str **buf,
 	struct cdr_object *it_cdr;
 	struct ast_var_t *variable;
 	const char *var;
-	RAII_VAR(char *, workspace, ast_malloc(256), ast_free);
+	char workspace[256];
 	int total = 0, x = 0, i;
 
-	if (!workspace) {
-		return 0;
-	}
-
 	if (!cdr) {
 		RAII_VAR(struct module_config *, mod_cfg,
 			 ao2_global_obj_ref(module_configs), ao2_cleanup);
diff --git a/main/cel.c b/main/cel.c
index 96547bb..0c1e37b 100644
--- a/main/cel.c
+++ b/main/cel.c
@@ -38,7 +38,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 427870 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/_private.h"
 
@@ -284,7 +284,7 @@ static struct aco_type *general_options[] = ACO_TYPES(&general_option);
  * \brief Map of ast_cel_event_type to strings
  */
 static const char * const cel_event_types[CEL_MAX_EVENT_IDS] = {
-	[0]                        = "ALL",
+	[AST_CEL_ALL]              = "ALL",
 	[AST_CEL_CHANNEL_START]    = "CHAN_START",
 	[AST_CEL_CHANNEL_END]      = "CHAN_END",
 	[AST_CEL_ANSWER]           = "ANSWER",
@@ -524,16 +524,13 @@ enum ast_cel_event_type ast_cel_str_to_event_type(const char *name)
 	unsigned int i;
 
 	for (i = 0; i < ARRAY_LEN(cel_event_types); i++) {
-		if (!cel_event_types[i]) {
-			continue;
-		}
-
-		if (!strcasecmp(name, cel_event_types[i])) {
+		if (cel_event_types[i] && !strcasecmp(name, cel_event_types[i])) {
 			return i;
 		}
 	}
 
-	return -1;
+	ast_log(LOG_ERROR, "Unknown event name '%s'\n", name);
+	return AST_CEL_INVALID_VALUE;
 }
 
 static int ast_cel_track_event(enum ast_cel_event_type et)
@@ -563,11 +560,10 @@ static int events_handler(const struct aco_option *opt, struct ast_variable *var
 
 		event_type = ast_cel_str_to_event_type(cur_event);
 
-		if (event_type == 0) {
+		if (event_type == AST_CEL_ALL) {
 			/* All events */
 			cfg->events = (int64_t) -1;
-		} else if (event_type == -1) {
-			ast_log(LOG_ERROR, "Unknown event name '%s'\n", cur_event);
+		} else if (event_type == AST_CEL_INVALID_VALUE) {
 			return -1;
 		} else {
 			cfg->events |= ((int64_t) 1 << event_type);
@@ -1513,10 +1509,7 @@ static void cel_engine_cleanup(void)
 	destroy_routes();
 	destroy_subscriptions();
 	STASIS_MESSAGE_TYPE_CLEANUP(cel_generic_type);
-}
 
-static void cel_engine_atexit(void)
-{
 	ast_cli_unregister(&cli_status);
 	aco_info_destroy(&cel_cfg_info);
 	ao2_global_obj_release(cel_configs);
@@ -1525,12 +1518,6 @@ static void cel_engine_atexit(void)
 	ao2_global_obj_release(cel_backends);
 }
 
-static void cel_engine_abort(void)
-{
-	cel_engine_cleanup();
-	cel_engine_atexit();
-}
-
 /*!
  * \brief Create the Stasis subscriptions for CEL
  */
@@ -1714,7 +1701,7 @@ int ast_cel_engine_init(void)
 	ao2_global_obj_replace_unref(cel_linkedids, container);
 	ao2_cleanup(container);
 	if (!container) {
-		cel_engine_abort();
+		cel_engine_cleanup();
 		return -1;
 	}
 
@@ -1723,17 +1710,17 @@ int ast_cel_engine_init(void)
 	ao2_global_obj_replace_unref(cel_dialstatus_store, container);
 	ao2_cleanup(container);
 	if (!container) {
-		cel_engine_abort();
+		cel_engine_cleanup();
 		return -1;
 	}
 
 	if (STASIS_MESSAGE_TYPE_INIT(cel_generic_type)) {
-		cel_engine_abort();
+		cel_engine_cleanup();
 		return -1;
 	}
 
 	if (ast_cli_register(&cli_status)) {
-		cel_engine_abort();
+		cel_engine_cleanup();
 		return -1;
 	}
 
@@ -1741,12 +1728,12 @@ int ast_cel_engine_init(void)
 	ao2_global_obj_replace_unref(cel_backends, container);
 	ao2_cleanup(container);
 	if (!container) {
-		cel_engine_abort();
+		cel_engine_cleanup();
 		return -1;
 	}
 
 	if (aco_info_init(&cel_cfg_info)) {
-		cel_engine_abort();
+		cel_engine_cleanup();
 		return -1;
 	}
 
@@ -1759,7 +1746,7 @@ int ast_cel_engine_init(void)
 		struct cel_config *cel_cfg = cel_config_alloc();
 
 		if (!cel_cfg) {
-			cel_engine_abort();
+			cel_engine_cleanup();
 			return -1;
 		}
 
@@ -1772,16 +1759,15 @@ int ast_cel_engine_init(void)
 	}
 
 	if (create_subscriptions()) {
-		cel_engine_abort();
+		cel_engine_cleanup();
 		return -1;
 	}
 
 	if (ast_cel_check_enabled() && create_routes()) {
-		cel_engine_abort();
+		cel_engine_cleanup();
 		return -1;
 	}
 
-	ast_register_atexit(cel_engine_atexit);
 	ast_register_cleanup(cel_engine_cleanup);
 	return 0;
 }
diff --git a/main/channel.c b/main/channel.c
index 9153491..6507ef9 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 429064 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/_private.h"
 
@@ -53,7 +53,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 429064 $")
 #include "asterisk/chanvars.h"
 #include "asterisk/linkedlists.h"
 #include "asterisk/indications.h"
-#include "asterisk/monitor.h"
 #include "asterisk/causes.h"
 #include "asterisk/callerid.h"
 #include "asterisk/utils.h"
@@ -75,6 +74,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 429064 $")
 #include "asterisk/bridge.h"
 #include "asterisk/test.h"
 #include "asterisk/stasis_channels.h"
+#include "asterisk/max_forwards.h"
 
 /*** DOCUMENTATION
  ***/
@@ -100,9 +100,6 @@ struct ast_epoll_data {
 #define MONITOR_DELAY	150 * 8		/*!< 150 ms of MONITORING DELAY */
 #endif
 
-/*! \brief Prevent new channel allocation if shutting down. */
-static int shutting_down;
-
 static int chancount;
 
 unsigned long global_fin, global_fout;
@@ -342,7 +339,7 @@ static char *complete_channeltypes(struct ast_cli_args *a)
 static char *handle_cli_core_show_channeltype(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
 	struct chanlist *cl = NULL;
-	struct ast_str *codec_buf = ast_str_alloca(256);
+	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 	switch (cmd) {
 	case CLI_INIT:
@@ -504,13 +501,9 @@ static int ast_channel_softhangup_cb(void *obj, void *arg, int flags)
 	return 0;
 }
 
-void ast_begin_shutdown(int hangup)
+void ast_softhangup_all(void)
 {
-	shutting_down = 1;
-
-	if (hangup) {
-		ao2_callback(channels, OBJ_NODATA | OBJ_MULTIPLE, ast_channel_softhangup_cb, NULL);
-	}
+	ao2_callback(channels, OBJ_NODATA | OBJ_MULTIPLE, ast_channel_softhangup_cb, NULL);
 }
 
 /*! \brief returns number of active/allocated channels */
@@ -524,18 +517,6 @@ int ast_undestroyed_channels(void)
 	return ast_atomic_fetchadd_int(&chancount, 0);
 }
 
-/*! \brief Cancel a shutdown in progress */
-void ast_cancel_shutdown(void)
-{
-	shutting_down = 0;
-}
-
-/*! \brief Returns non-zero if Asterisk is being shut down */
-int ast_shutting_down(void)
-{
-	return shutting_down;
-}
-
 /*! \brief Set when to hangup channel */
 void ast_channel_setwhentohangup_tv(struct ast_channel *chan, struct timeval offset)
 {
@@ -891,6 +872,7 @@ __ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char
 	ast_channel_hold_state_set(tmp, AST_CONTROL_UNHOLD);
 
 	ast_channel_streamid_set(tmp, -1);
+	ast_channel_vstreamid_set(tmp, -1);
 
 	ast_channel_fin_set(tmp, global_fin);
 	ast_channel_fout_set(tmp, global_fout);
@@ -1212,8 +1194,8 @@ int ast_queue_hangup_with_cause(struct ast_channel *chan, int cause)
 
 int ast_queue_hold(struct ast_channel *chan, const char *musicclass)
 {
-	RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
 	struct ast_frame f = { AST_FRAME_CONTROL, .subclass.integer = AST_CONTROL_HOLD };
+	struct ast_json *blob = NULL;
 	int res;
 
 	if (!ast_strlen_zero(musicclass)) {
@@ -1228,6 +1210,8 @@ int ast_queue_hold(struct ast_channel *chan, const char *musicclass)
 
 	res = ast_queue_frame(chan, &f);
 
+	ast_json_unref(blob);
+
 	return res;
 }
 
@@ -2843,14 +2827,14 @@ int __ast_answer(struct ast_channel *chan, unsigned int delay)
 				}
 			}
 
-			if (res == 0) {
-				ast_channel_lock(chan);
-				while ((cur = AST_LIST_REMOVE_HEAD(&frames, frame_list))) {
+			ast_channel_lock(chan);
+			while ((cur = AST_LIST_REMOVE_HEAD(&frames, frame_list))) {
+				if (res == 0) {
 					ast_queue_frame_head(chan, cur);
-					ast_frfree(cur);
 				}
-				ast_channel_unlock(chan);
+				ast_frfree(cur);
 			}
+			ast_channel_unlock(chan);
 		} while (0);
 		break;
 	default:
@@ -3922,11 +3906,7 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
 		switch (f->frametype) {
 		case AST_FRAME_CONTROL:
 			if (f->subclass.integer == AST_CONTROL_ANSWER) {
-				if (!ast_test_flag(ast_channel_flags(chan), AST_FLAG_OUTGOING)) {
-					ast_debug(1, "Ignoring answer on an inbound call!\n");
-					ast_frfree(f);
-					f = &ast_null_frame;
-				} else if (prestate == AST_STATE_UP && ast_channel_is_bridged(chan)) {
+				if (prestate == AST_STATE_UP && ast_channel_is_bridged(chan)) {
 					ast_debug(1, "Dropping duplicate answer!\n");
 					ast_frfree(f);
 					f = &ast_null_frame;
@@ -4131,78 +4111,133 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
 					ast_frfree(f);
 					f = &ast_null_frame;
 				}
-			} else if ((f->frametype == AST_FRAME_VOICE) && ast_format_cap_iscompatible_format(ast_channel_nativeformats(chan), f->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
-				/* This frame is not one of the current native formats -- drop it on the floor */
-				struct ast_str *codec_buf = ast_str_alloca(64);
-				ast_log(LOG_NOTICE, "Dropping incompatible voice frame on %s of format %s since our native format has changed to %s\n",
-					ast_channel_name(chan), ast_format_get_name(f->subclass.format), ast_format_cap_get_names(ast_channel_nativeformats(chan), &codec_buf));
-				ast_frfree(f);
-				f = &ast_null_frame;
-			} else if ((f->frametype == AST_FRAME_VOICE)) {
-				/* Send frame to audiohooks if present */
-				if (ast_channel_audiohooks(chan)) {
-					struct ast_frame *old_frame = f;
-					f = ast_audiohook_write_list(chan, ast_channel_audiohooks(chan), AST_AUDIOHOOK_DIRECTION_READ, f);
-					if (old_frame != f)
-						ast_frfree(old_frame);
+				break;
+			}
+			if (f->frametype != AST_FRAME_VOICE) {
+				break;
+			}
+			if (ast_format_cmp(f->subclass.format, ast_channel_rawreadformat(chan)) != AST_FORMAT_CMP_EQUAL
+				&& ast_format_cmp(f->subclass.format, ast_channel_readformat(chan)) != AST_FORMAT_CMP_EQUAL) {
+				struct ast_format *core_format;
+
+				/*
+				 * Note: This frame may not be one of the current native
+				 * formats.  We may have gotten it out of the read queue from
+				 * a previous multi-frame translation, from a framehook
+				 * injected frame, or the device we're talking to isn't
+				 * respecting negotiated formats.  Regardless we will accept
+				 * all frames.
+				 *
+				 * Update the read translation path to handle the new format
+				 * that just came in.  If the core wants slinear we need to
+				 * setup a new translation path because the core is usually
+				 * doing something with the audio itself and may not handle
+				 * any other format.  e.g., Softmix bridge, holding bridge
+				 * announcer channel, recording, AMD...  Otherwise, we'll
+				 * setup to pass the frame as is to the core.  In this case
+				 * the core doesn't care.  The channel is likely in
+				 * autoservice, safesleep, or the channel is in a bridge.
+				 * Let the bridge technology deal with format compatibility
+				 * between the channels in the bridge.
+				 *
+				 * Beware of the transcode_via_slin and genericplc options as
+				 * they force any transcoding to go through slin on a bridge.
+				 * Unfortunately transcode_via_slin is enabled by default and
+				 * genericplc is enabled in the codecs.conf.sample file.
+				 *
+				 * XXX Only updating translation to slinear frames has some
+				 * corner cases if slinear is one of the native formats and
+				 * there are different sample rates involved.  We might wind
+				 * up with conflicting translation paths between channels
+				 * where the read translation path on this channel reduces
+				 * the sample rate followed by a write translation path on
+				 * the peer channel that increases the sample rate.
+				 */
+				core_format = ast_channel_readformat(chan);
+				if (!ast_format_cache_is_slinear(core_format)) {
+					core_format = f->subclass.format;
+				}
+				if (ast_set_read_format_path(chan, f->subclass.format, core_format)) {
+					/* Drop frame.  We couldn't make it compatible with the core. */
+					ast_frfree(f);
+					f = &ast_null_frame;
+					break;
+				}
+			}
+			/* Send frame to audiohooks if present */
+			if (ast_channel_audiohooks(chan)) {
+				struct ast_frame *old_frame = f;
+
+				f = ast_audiohook_write_list(chan, ast_channel_audiohooks(chan), AST_AUDIOHOOK_DIRECTION_READ, f);
+				if (old_frame != f) {
+					ast_frfree(old_frame);
 				}
-				if (ast_channel_monitor(chan) && ast_channel_monitor(chan)->read_stream ) {
-					/* XXX what does this do ? */
+			}
+			if (ast_channel_monitor(chan) && ast_channel_monitor(chan)->read_stream) {
+				/* XXX what does this do ? */
 #ifndef MONITOR_CONSTANT_DELAY
-					int jump = ast_channel_outsmpl(chan) - ast_channel_insmpl(chan) - 4 * f->samples;
-					if (jump >= 0) {
-						jump = calc_monitor_jump((ast_channel_outsmpl(chan) - ast_channel_insmpl(chan)),
-						                         ast_format_get_sample_rate(f->subclass.format),
-						                         ast_format_get_sample_rate(ast_channel_monitor(chan)->read_stream->fmt->format));
-						if (ast_seekstream(ast_channel_monitor(chan)->read_stream, jump, SEEK_FORCECUR) == -1) {
-							ast_log(LOG_WARNING, "Failed to perform seek in monitoring read stream, synchronization between the files may be broken\n");
-						}
-						ast_channel_insmpl_set(chan, ast_channel_insmpl(chan) + (ast_channel_outsmpl(chan) - ast_channel_insmpl(chan)) + f->samples);
-					} else {
-						ast_channel_insmpl_set(chan, ast_channel_insmpl(chan) + f->samples);
+				int jump = ast_channel_outsmpl(chan) - ast_channel_insmpl(chan) - 4 * f->samples;
+				if (jump >= 0) {
+					jump = calc_monitor_jump((ast_channel_outsmpl(chan) - ast_channel_insmpl(chan)),
+						ast_format_get_sample_rate(f->subclass.format),
+						ast_format_get_sample_rate(ast_channel_monitor(chan)->read_stream->fmt->format));
+					if (ast_seekstream(ast_channel_monitor(chan)->read_stream, jump, SEEK_FORCECUR) == -1) {
+						ast_log(LOG_WARNING, "Failed to perform seek in monitoring read stream, synchronization between the files may be broken\n");
 					}
+					ast_channel_insmpl_set(chan, ast_channel_insmpl(chan) + (ast_channel_outsmpl(chan) - ast_channel_insmpl(chan)) + f->samples);
+				} else {
+					ast_channel_insmpl_set(chan, ast_channel_insmpl(chan) + f->samples);
+				}
 #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(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)
-							ast_log(LOG_WARNING, "Failed to perform seek in monitoring read stream, synchronization between the files may be broken\n");
-						ast_channel_insmpl(chan) += ast_channel_outsmpl(chan) - ast_channel_insmpl(chan);
-					} else
-						ast_channel_insmpl(chan) += f->samples;
-#endif
-					if (ast_channel_monitor(chan)->state == AST_MONITOR_RUNNING) {
-						if (ast_writestream(ast_channel_monitor(chan)->read_stream, f) < 0)
-							ast_log(LOG_WARNING, "Failed to write data to channel monitor read stream\n");
+				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(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) {
+						ast_log(LOG_WARNING, "Failed to perform seek in monitoring read stream, synchronization between the files may be broken\n");
 					}
+					ast_channel_insmpl(chan) += ast_channel_outsmpl(chan) - ast_channel_insmpl(chan);
+				} else {
+					ast_channel_insmpl(chan) += f->samples;
+				}
+#endif
+				if (ast_channel_monitor(chan)->state == AST_MONITOR_RUNNING) {
+					if (ast_writestream(ast_channel_monitor(chan)->read_stream, f) < 0)
+						ast_log(LOG_WARNING, "Failed to write data to channel monitor read stream\n");
 				}
+			}
 
-				if (ast_channel_readtrans(chan) && (f = ast_translate(ast_channel_readtrans(chan), f, 1)) == NULL) {
+			if (ast_channel_readtrans(chan)
+				&& ast_format_cmp(f->subclass.format, ast_channel_rawreadformat(chan)) == AST_FORMAT_CMP_EQUAL) {
+				f = ast_translate(ast_channel_readtrans(chan), f, 1);
+				if (!f) {
 					f = &ast_null_frame;
 				}
+			}
 
-				/* it is possible for the translation process on chan->readtrans to have
-				   produced multiple frames from the single input frame we passed it; if
-				   this happens, queue the additional frames *before* the frames we may
-				   have queued earlier. if the readq was empty, put them at the head of
-				   the queue, and if it was not, put them just after the frame that was
-				   at the end of the queue.
-				*/
-				if (AST_LIST_NEXT(f, frame_list)) {
-					if (!readq_tail) {
-						ast_queue_frame_head(chan, AST_LIST_NEXT(f, frame_list));
-					} else {
-						__ast_queue_frame(chan, AST_LIST_NEXT(f, frame_list), 0, readq_tail);
-					}
-					ast_frfree(AST_LIST_NEXT(f, frame_list));
-					AST_LIST_NEXT(f, frame_list) = NULL;
+			/*
+			 * It is possible for the translation process on the channel to have
+			 * produced multiple frames from the single input frame we passed it; if
+			 * this happens, queue the additional frames *before* the frames we may
+			 * have queued earlier. if the readq was empty, put them at the head of
+			 * the queue, and if it was not, put them just after the frame that was
+			 * at the end of the queue.
+			 */
+			if (AST_LIST_NEXT(f, frame_list)) {
+				if (!readq_tail) {
+					ast_queue_frame_head(chan, AST_LIST_NEXT(f, frame_list));
+				} else {
+					__ast_queue_frame(chan, AST_LIST_NEXT(f, frame_list), 0, readq_tail);
 				}
-
-				/* Run generator sitting on the line if timing device not available
-				* and synchronous generation of outgoing frames is necessary       */
-				ast_read_generator_actions(chan, f);
+				ast_frfree(AST_LIST_NEXT(f, frame_list));
+				AST_LIST_NEXT(f, frame_list) = NULL;
 			}
+
+			/*
+			 * Run generator sitting on the line if timing device not available
+			 * and synchronous generation of outgoing frames is necessary
+			 */
+			ast_read_generator_actions(chan, f);
 			break;
 		default:
 			/* Just pass it on! */
@@ -4453,7 +4488,7 @@ int ast_indicate_data(struct ast_channel *chan, int _condition,
 
 	/* Don't bother if the channel is about to go away, anyway. */
 	if ((ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE)
-			|| ast_check_hangup(chan))
+			|| (ast_check_hangup(chan) && !ast_channel_is_leaving_bridge(chan)))
 		&& condition != AST_CONTROL_MASQUERADE_NOTIFY) {
 		res = -1;
 		goto indicate_cleanup;
@@ -4622,6 +4657,11 @@ int ast_indicate_data(struct ast_channel *chan, int _condition,
 		/* We have a tone to play, yay. */
 		ast_debug(1, "Driver for channel '%s' does not support indication %u, emulating it\n", ast_channel_name(chan), condition);
 		res = ast_playtones_start(chan, 0, ts->data, 1);
+		if (!res) {
+			ast_test_suite_event_notify("RINGING_INBAND",
+					"Channel: %s\r\n",
+					ast_channel_name(chan));
+		}
 		ts = ast_tone_zone_sound_unref(ts);
 	}
 
@@ -5051,29 +5091,35 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
 		}
 
 		/* If the frame is in the raw write format, then it's easy... just use the frame - otherwise we will have to translate */
-		if (ast_format_cmp(fr->subclass.format, ast_channel_rawwriteformat(chan)) != AST_FORMAT_CMP_NOT_EQUAL) {
+		if (ast_format_cmp(fr->subclass.format, ast_channel_rawwriteformat(chan)) == AST_FORMAT_CMP_EQUAL) {
 			f = fr;
 		} else {
-			if ((ast_format_cap_iscompatible_format(ast_channel_nativeformats(chan), fr->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) &&
-			    (ast_format_cmp(ast_channel_writeformat(chan), fr->subclass.format) != AST_FORMAT_CMP_EQUAL)) {
-				struct ast_str *codec_buf = ast_str_alloca(64);
+			if (ast_format_cmp(ast_channel_writeformat(chan), fr->subclass.format) != AST_FORMAT_CMP_EQUAL) {
+				struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 				/*
-				 * XXX Something is not right.  We are not compatible with this
-				 * frame.  Bad things can happen.  Problems range from no audio,
-				 * one-way audio, to unexplained line hangups.  As a last resort
-				 * try to adjust the format.  Ideally, we do not want to do this
-				 * because it indicates a deeper problem.  For now, we log these
-				 * events to reduce user impact and help identify the problem
-				 * areas.
+				 * We are not setup to write this frame.  Things may have changed
+				 * on the peer side of the world and we try to adjust the format to
+				 * make it compatible again.  However, bad things can happen if we
+				 * cannot setup a new translation path.  Problems range from no
+				 * audio, one-way audio, to garbled audio.  The best we can do is
+				 * request the call to hangup since we could not make it compatible.
+				 *
+				 * Being continuously spammed by this message likely indicates a
+				 * problem with the peer because it cannot make up its mind about
+				 * which format to use.
 				 */
-				ast_log(LOG_WARNING, "Codec mismatch on channel %s setting write format to %s from %s native formats %s\n",
-					ast_channel_name(chan), ast_format_get_name(fr->subclass.format), ast_format_get_name(ast_channel_writeformat(chan)),
+				ast_debug(1, "Channel %s changing write format from %s to %s, native formats %s\n",
+					ast_channel_name(chan),
+					ast_format_get_name(ast_channel_writeformat(chan)),
+					ast_format_get_name(fr->subclass.format),
 					ast_format_cap_get_names(ast_channel_nativeformats(chan), &codec_buf));
-				ast_set_write_format(chan, fr->subclass.format);
+				if (ast_set_write_format(chan, fr->subclass.format)) {
+					/* Could not handle the new write format.  Induce a hangup. */
+					break;
+				}
 			}
-
-			f = (ast_channel_writetrans(chan)) ? ast_translate(ast_channel_writetrans(chan), fr, 0) : fr;
+			f = ast_channel_writetrans(chan) ? ast_translate(ast_channel_writetrans(chan), fr, 0) : fr;
 		}
 
 		if (!f) {
@@ -5233,6 +5279,42 @@ done:
 	return res;
 }
 
+int ast_set_read_format_path(struct ast_channel *chan, struct ast_format *raw_format, struct ast_format *core_format)
+{
+	struct ast_trans_pvt *trans_old;
+	struct ast_trans_pvt *trans_new;
+
+	if (ast_format_cmp(ast_channel_rawreadformat(chan), raw_format) == AST_FORMAT_CMP_EQUAL
+		&& ast_format_cmp(ast_channel_readformat(chan), core_format) == AST_FORMAT_CMP_EQUAL) {
+		/* Nothing to setup */
+		return 0;
+	}
+
+	ast_debug(1, "Channel %s setting read format path: %s -> %s\n",
+		ast_channel_name(chan),
+		ast_format_get_name(raw_format),
+		ast_format_get_name(core_format));
+
+	/* Setup new translation path. */
+	if (ast_format_cmp(raw_format, core_format) != AST_FORMAT_CMP_EQUAL) {
+		trans_new = ast_translator_build_path(core_format, raw_format);
+		if (!trans_new) {
+			return -1;
+		}
+	} else {
+		/* No translation needed. */
+		trans_new = NULL;
+	}
+	trans_old = ast_channel_readtrans(chan);
+	if (trans_old) {
+		ast_translator_free_path(trans_old);
+	}
+	ast_channel_readtrans_set(chan, trans_new);
+	ast_channel_set_rawreadformat(chan, raw_format);
+	ast_channel_set_readformat(chan, core_format);
+	return 0;
+}
+
 struct set_format_access {
 	const char *direction;
 	struct ast_trans_pvt *(*get_trans)(const struct ast_channel *chan);
@@ -5285,7 +5367,14 @@ static int set_format(struct ast_channel *chan, struct ast_format_cap *cap_set,
 		access = &set_format_access_write;
 	}
 
-	best_set_fmt = ast_format_cap_get_format(cap_set, 0);
+	best_set_fmt = ast_format_cap_get_best_by_type(cap_set, AST_MEDIA_TYPE_AUDIO);
+	if (!best_set_fmt) {
+		/*
+		 * Not setting any audio formats?
+		 * Assume a call without any sounds (video, text)
+		 */
+		return 0;
+	}
 
 	/* See if the underlying channel driver is capable of performing transcoding for us */
 	res = ast_channel_setoption(chan, access->setoption,
@@ -5347,15 +5436,16 @@ static int set_format(struct ast_channel *chan, struct ast_format_cap *cap_set,
 		res = ast_translator_best_choice(cap_native, cap_set, &best_native_fmt, &best_set_fmt);
 	}
 	if (res < 0) {
-		struct ast_str *codec_from = ast_str_alloca(64);
-		struct ast_str *codec_to = ast_str_alloca(64);
+		struct ast_str *codec_native = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+		struct ast_str *codec_set = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
-		ast_format_cap_get_names(cap_native, &codec_from);
+		ast_format_cap_get_names(cap_native, &codec_native);
 		ast_channel_unlock(chan);
-		ast_format_cap_get_names(cap_set, &codec_to);
+		ast_format_cap_get_names(cap_set, &codec_set);
 
-		ast_log(LOG_WARNING, "Unable to find a codec translation path from %s to %s\n",
-			ast_str_buffer(codec_from), ast_str_buffer(codec_to));
+		ast_log(LOG_WARNING, "Unable to find a codec translation path: %s -> %s\n",
+			ast_str_buffer(direction ? codec_set : codec_native),
+			ast_str_buffer(direction ? codec_native : codec_set));
 		return -1;
 	}
 
@@ -5399,10 +5489,11 @@ static int set_format(struct ast_channel *chan, struct ast_format_cap *cap_set,
 		access->set_format(chan, best_set_fmt);
 		access->set_rawformat(chan, best_native_fmt);
 
-		ast_debug(1, "Set channel %s to %s format %s\n",
+		ast_debug(1, "Channel %s setting %s format path: %s -> %s\n",
 			ast_channel_name(chan),
 			access->direction,
-			ast_format_get_name(best_set_fmt));
+			ast_format_get_name(direction ? best_set_fmt : best_native_fmt),
+			ast_format_get_name(direction ? best_native_fmt : best_set_fmt));
 	}
 
 	ast_channel_unlock(chan);
@@ -5532,6 +5623,7 @@ static void call_forward_inherit(struct ast_channel *new_chan, struct ast_channe
 	ast_channel_lock_both(parent, new_chan);
 	ast_channel_inherit_variables(parent, new_chan);
 	ast_channel_datastore_inherit(parent, new_chan);
+	ast_max_forwards_decrement(new_chan);
 	ast_channel_unlock(new_chan);
 	ast_channel_unlock(parent);
 }
@@ -5651,6 +5743,7 @@ struct ast_channel *__ast_request_and_dial(const char *type, struct ast_format_c
 			ast_channel_lock_both(oh->parent_channel, chan);
 			ast_channel_inherit_variables(oh->parent_channel, chan);
 			ast_channel_datastore_inherit(oh->parent_channel, chan);
+			ast_max_forwards_decrement(chan);
 			ast_channel_unlock(oh->parent_channel);
 			ast_channel_unlock(chan);
 		}
@@ -5886,8 +5979,8 @@ struct ast_channel *ast_request(const char *type, struct ast_format_cap *request
 			res = ast_translator_best_choice(tmp_cap, chan->tech->capabilities, &tmp_fmt, &best_audio_fmt);
 			ao2_ref(tmp_cap, -1);
 			if (res < 0) {
-				struct ast_str *tech_codecs = ast_str_alloca(64);
-				struct ast_str *request_codecs = ast_str_alloca(64);
+				struct ast_str *tech_codecs = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+				struct ast_str *request_codecs = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 				ast_log(LOG_WARNING, "No translator path exists for channel type %s (native %s) to %s\n", type,
 					ast_format_cap_get_names(chan->tech->capabilities, &tech_codecs),
@@ -5902,9 +5995,10 @@ struct ast_channel *ast_request(const char *type, struct ast_format_cap *request
 			return NULL;
 
 		/* XXX Only the audio format calculated as being the best for translation
-		 * purposes is used for the request. This needs to be re-evaluated.  It may be
-		 * a better choice to send all the audio formats capable of being translated
-		 * during the request and allow the channel drivers to pick the best one. */
+		 * purposes is used for the request. This is because we don't have the ability
+		 * to signal to the initiator which one of their codecs that was offered is
+		 * the one that was selected, particularly in a chain of Local channels.
+		 */
 		joint_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
 		if (!joint_cap) {
 			return NULL;
@@ -6217,15 +6311,17 @@ static int ast_channel_make_compatible_helper(struct ast_channel *from, struct a
 	RAII_VAR(struct ast_format *, best_dst_fmt, NULL, ao2_cleanup);
 	int no_path;
 
-	ast_channel_lock_both(from, to);
+	/*
+	 * We cannot short circuit this code because it is possible to ask
+	 * to make compatible two channels that are "compatible" because
+	 * they already have translation paths setup but together make for
+	 * a sub-optimal path.  e.g., The From channel has g722 -> ulaw
+	 * and the To channel has ulaw -> g722.  They are "compatible" but
+	 * together the translations are unnecessary and the audio loses
+	 * fidelity in the process.
+	 */
 
-	if ((ast_format_cmp(ast_channel_readformat(from), ast_channel_writeformat(to)) != AST_FORMAT_CMP_NOT_EQUAL) &&
-		(ast_format_cmp(ast_channel_readformat(to), ast_channel_writeformat(from)) != AST_FORMAT_CMP_NOT_EQUAL)) {
-		/* Already compatible!  Moving on ... */
-		ast_channel_unlock(to);
-		ast_channel_unlock(from);
-		return 0;
-	}
+	ast_channel_lock_both(from, to);
 
 	src_cap = ast_channel_nativeformats(from); /* shallow copy, do not destroy */
 	dst_cap = ast_channel_nativeformats(to);   /* shallow copy, do not destroy */
@@ -7314,7 +7410,7 @@ int ast_moh_start(struct ast_channel *chan, const char *mclass, const char *inte
 
 	ast_verb(3, "Music class %s requested but no musiconhold loaded.\n", mclass ? mclass : (interpclass ? interpclass : "default"));
 
-	return 0;
+	return -1;
 }
 
 void ast_moh_stop(struct ast_channel *chan)
@@ -7663,7 +7759,7 @@ void ast_channels_init(void)
 
 	ast_plc_reload();
 
-	ast_register_atexit(channels_shutdown);
+	ast_register_cleanup(channels_shutdown);
 
 }
 
diff --git a/main/channel_internal_api.c b/main/channel_internal_api.c
index d163b5d..db5f3c0 100644
--- a/main/channel_internal_api.c
+++ b/main/channel_internal_api.c
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 429062 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <unistd.h>
 #include <fcntl.h>
@@ -1210,7 +1210,14 @@ void ast_channel_named_pickupgroups_set(struct ast_channel *chan, struct ast_nam
 int ast_channel_alert_write(struct ast_channel *chan)
 {
 	char blah = 0x7F;
-	return ast_channel_alert_writable(chan) && write(chan->alertpipe[1], &blah, sizeof(blah)) != sizeof(blah);
+
+	if (!ast_channel_alert_writable(chan)) {
+		errno = EBADF;
+		return 0;
+	}
+	/* preset errno in case returned size does not match */
+	errno = EPIPE;
+	return write(chan->alertpipe[1], &blah, sizeof(blah)) != sizeof(blah);
 }
 
 ast_alert_status_t ast_channel_internal_alert_read(struct ast_channel *chan)
@@ -1261,9 +1268,11 @@ void ast_channel_internal_alertpipe_close(struct ast_channel *chan)
 {
 	if (ast_channel_internal_alert_readable(chan)) {
 		close(chan->alertpipe[0]);
+		chan->alertpipe[0] = -1;
 	}
 	if (ast_channel_alert_writable(chan)) {
 		close(chan->alertpipe[1]);
+		chan->alertpipe[1] = -1;
 	}
 }
 
@@ -1457,6 +1466,10 @@ struct ast_channel *__ast_channel_internal_alloc(void (*destructor)(void *obj),
 	tmp = ao2_alloc(sizeof(*tmp), destructor);
 #endif
 
+	if (!tmp) {
+		return NULL;
+	}
+
 	if ((ast_string_field_init(tmp, 128))) {
 		return ast_channel_unref(tmp);
 	}
diff --git a/main/chanvars.c b/main/chanvars.c
index 7e271ff..37714e9 100644
--- a/main/chanvars.c
+++ b/main/chanvars.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 424964 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/chanvars.h"
 #include "asterisk/strings.h"
diff --git a/main/cli.c b/main/cli.c
index 7823689..7f86eab 100644
--- a/main/cli.c
+++ b/main/cli.c
@@ -38,7 +38,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 422665 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/_private.h"
 #include "asterisk/paths.h"	/* use ast_config_AST_MODULE_DIR */
@@ -487,7 +487,7 @@ static char *handle_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args
 				return ast_strdup("atleast");
 			}
 #if !defined(LOW_MEMORY)
-		} else if ((a->pos == 4 && !atleast && strcasecmp(argv3, "off"))
+		} else if ((a->pos == 4 && !atleast && strcasecmp(argv3, "off") && strcasecmp(argv3, "channel"))
 			|| (a->pos == 5 && atleast)) {
 			const char *pos = S_OR(a->argv[a->pos], "");
 
@@ -802,7 +802,7 @@ static void print_uptimestr(int fd, struct timeval timeval, const char *prefix,
 #define DAY (HOUR*24)
 #define WEEK (DAY*7)
 #define YEAR (DAY*365)
-#define NEEDCOMMA(x) ((x)? ",": "")	/* define if we need a comma */
+#define NEEDCOMMA(x) ((x) ? ", " : "")	/* define if we need a comma */
 	if (timeval.tv_sec < 0)	/* invalid, nothing to show */
 		return;
 
@@ -814,31 +814,33 @@ static void print_uptimestr(int fd, struct timeval timeval, const char *prefix,
 	if (timeval.tv_sec > YEAR) {
 		x = (timeval.tv_sec / YEAR);
 		timeval.tv_sec -= (x * YEAR);
-		ast_str_append(&out, 0, "%d year%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
+		ast_str_append(&out, 0, "%d year%s%s", x, ESS(x), NEEDCOMMA(timeval.tv_sec));
 	}
 	if (timeval.tv_sec > WEEK) {
 		x = (timeval.tv_sec / WEEK);
 		timeval.tv_sec -= (x * WEEK);
-		ast_str_append(&out, 0, "%d week%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
+		ast_str_append(&out, 0, "%d week%s%s", x, ESS(x), NEEDCOMMA(timeval.tv_sec));
 	}
 	if (timeval.tv_sec > DAY) {
 		x = (timeval.tv_sec / DAY);
 		timeval.tv_sec -= (x * DAY);
-		ast_str_append(&out, 0, "%d day%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
+		ast_str_append(&out, 0, "%d day%s%s", x, ESS(x), NEEDCOMMA(timeval.tv_sec));
 	}
 	if (timeval.tv_sec > HOUR) {
 		x = (timeval.tv_sec / HOUR);
 		timeval.tv_sec -= (x * HOUR);
-		ast_str_append(&out, 0, "%d hour%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
+		ast_str_append(&out, 0, "%d hour%s%s", x, ESS(x), NEEDCOMMA(timeval.tv_sec));
 	}
 	if (timeval.tv_sec > MINUTE) {
 		x = (timeval.tv_sec / MINUTE);
 		timeval.tv_sec -= (x * MINUTE);
-		ast_str_append(&out, 0, "%d minute%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
+		ast_str_append(&out, 0, "%d minute%s%s", x, ESS(x), NEEDCOMMA(timeval.tv_sec));
 	}
 	x = timeval.tv_sec;
-	if (x > 0 || ast_str_strlen(out) == 0)	/* if there is nothing, print 0 seconds */
-		ast_str_append(&out, 0, "%d second%s ", x, ESS(x));
+	if (x > 0 || ast_str_strlen(out) == 0) {
+		/* if there is nothing, print 0 seconds */
+		ast_str_append(&out, 0, "%d second%s", x, ESS(x));
+	}
 	ast_cli(fd, "%s: %s\n", prefix, ast_str_buffer(out));
 }
 
@@ -1074,10 +1076,12 @@ static char *handle_chanlist(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
 				char locbuf[40] = "(None)";
 				char appdata[40] = "(None)";
 
-				if (!cs->context && !cs->exten)
+				if (!ast_strlen_zero(cs->context) && !ast_strlen_zero(cs->exten)) {
 					snprintf(locbuf, sizeof(locbuf), "%s@%s:%d", cs->exten, cs->context, cs->priority);
-				if (cs->appl)
+				}
+				if (!ast_strlen_zero(cs->appl)) {
 					snprintf(appdata, sizeof(appdata), "%s(%s)", cs->appl, S_OR(cs->data, ""));
+				}
 				ast_cli(a->fd, FORMAT_STRING, cs->name, locbuf, ast_state2str(cs->state), appdata);
 			}
 		}
@@ -1526,7 +1530,7 @@ static char *handle_showchan(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
 	struct ast_var_t *var;
 	struct ast_str *write_transpath = ast_str_alloca(256);
 	struct ast_str *read_transpath = ast_str_alloca(256);
-	struct ast_str *codec_buf = ast_str_alloca(64);
+	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 	struct ast_bridge *bridge;
 	struct ast_callid *callid;
 	char callid_buf[32];
@@ -1645,7 +1649,7 @@ static char *handle_showchan(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
 		ast_str_buffer(write_transpath),
 		ast_str_strlen(read_transpath) ? "Yes" : "No",
 		ast_str_buffer(read_transpath),
-		ast_channel_whentohangup(chan)->tv_sec,
+		(long)ast_channel_whentohangup(chan)->tv_sec,
 		cdrtime,
 		bridge ? bridge->uniqueid : "(Not bridged)",
 		ast_channel_context(chan),
@@ -2027,7 +2031,7 @@ static void cli_shutdown(void)
 void ast_builtins_init(void)
 {
 	ast_cli_register_multiple(cli_cli, ARRAY_LEN(cli_cli));
-	ast_register_atexit(cli_shutdown);
+	ast_register_cleanup(cli_shutdown);
 }
 
 /*!
diff --git a/main/codec.c b/main/codec.c
index 0f5f49f..543d4d0 100644
--- a/main/codec.c
+++ b/main/codec.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419044 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/logger.h"
 #include "asterisk/codec.h"
@@ -245,7 +245,7 @@ int ast_codec_init(void)
 	}
 
 	ast_cli_register_multiple(codec_cli, ARRAY_LEN(codec_cli));
-	ast_register_atexit(codec_shutdown);
+	ast_register_cleanup(codec_shutdown);
 
 	return 0;
 }
@@ -296,8 +296,8 @@ int __ast_codec_register(struct ast_codec *codec, struct ast_module *mod)
 
 	ao2_link_flags(codecs, codec_new, OBJ_NOLOCK);
 
-	/* Once registered a codec can not be unregistered, and the module must persist */
-	ast_module_ref(mod);
+	/* Once registered a codec can not be unregistered, and the module must persist until shutdown */
+	ast_module_shutdown_ref(mod);
 
 	ast_verb(2, "Registered '%s' codec '%s' at sample rate '%u' with id '%u'\n",
 		ast_codec_media_type2str(codec->type), codec->name, codec->sample_rate, codec_new->id);
@@ -355,10 +355,7 @@ unsigned int ast_codec_samples_count(struct ast_frame *frame)
 		return 0;
 	}
 
-	/* BUGBUG - why not just get the codec pointer off the format?
-	This is a bit roundabout
-	*/
-	codec = ast_codec_get_by_id(ast_format_get_codec_id(frame->subclass.format));
+	codec = ast_format_get_codec(frame->subclass.format);
 
 	if (codec->samples_count) {
 		samples = codec->samples_count(frame);
diff --git a/main/codec_builtin.c b/main/codec_builtin.c
index a77e6b1..346b47b 100644
--- a/main/codec_builtin.c
+++ b/main/codec_builtin.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419044 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/logger.h"
 #include "asterisk/astobj2.h"
@@ -594,7 +594,7 @@ static struct ast_codec ilbc = {
 	.type = AST_MEDIA_TYPE_AUDIO,
 	.sample_rate = 8000,
 	.minimum_ms = 30,
-	.maximum_ms = 30,
+	.maximum_ms = 300,
 	.default_ms = 30,
 	.minimum_bytes = 50,
 	.samples_count = ilbc_samples,
@@ -776,7 +776,7 @@ static struct ast_codec t140 = {
 		struct ast_codec *__codec_ ## __LINE__; \
 		res |= __ast_codec_register(&(codec), NULL); \
 		__codec_ ## __LINE__ = ast_codec_get((codec).name, (codec).type, (codec).sample_rate); \
-		__fmt_ ## __LINE__ = ast_format_create(__codec_ ## __LINE__); \
+		__fmt_ ## __LINE__ = __codec_ ## __LINE__ ? ast_format_create(__codec_ ## __LINE__) : NULL; \
 		res |= ast_format_cache_set(__fmt_ ## __LINE__); \
 		ao2_ref(__fmt_ ## __LINE__, -1); \
 		ao2_ref(__codec_ ## __LINE__, -1); \
diff --git a/main/config.c b/main/config.c
index 73f0aa5..eefc8ca 100644
--- a/main/config.c
+++ b/main/config.c
@@ -32,7 +32,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428734 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/paths.h"	/* use ast_config_AST_CONFIG_DIR */
 #include "asterisk/network.h"	/* we do some sockaddr manipulation here */
@@ -71,7 +71,7 @@ static char *extconfig_conf = "extconfig.conf";
 
 static struct ao2_container *cfg_hooks;
 static void config_hook_exec(const char *filename, const char *module, const struct ast_config *cfg);
-inline struct ast_variable *variable_list_switch(struct ast_variable *l1, struct ast_variable *l2);
+static inline struct ast_variable *variable_list_switch(struct ast_variable *l1, struct ast_variable *l2);
 static int does_category_match(struct ast_category *cat, const char *category_name, const char *match);
 
 /*! \brief Structure to keep comments for rewriting configuration files */
@@ -614,7 +614,7 @@ struct ast_variable *ast_variable_browse(const struct ast_config *config, const
 	return (cat) ? cat->root : NULL;
 }
 
-inline struct ast_variable *variable_list_switch(struct ast_variable *l1, struct ast_variable *l2)
+static inline struct ast_variable *variable_list_switch(struct ast_variable *l1, struct ast_variable *l2)
 {
     l1->next = l2->next;
     l2->next = l1;
@@ -735,6 +735,19 @@ const char *ast_variable_find_in_list(const struct ast_variable *list, const cha
 	return NULL;
 }
 
+const char *ast_variable_find_last_in_list(const struct ast_variable *list, const char *variable)
+{
+	const struct ast_variable *v;
+	const char *found = NULL;
+
+	for (v = list; v; v = v->next) {
+		if (!strcasecmp(variable, v->name)) {
+			found = v->value;
+		}
+	}
+	return found;
+}
+
 static struct ast_variable *variable_clone(const struct ast_variable *old)
 {
 	struct ast_variable *new = ast_variable_new(old->name, old->value, old->file);
@@ -1238,20 +1251,27 @@ void ast_category_rename(struct ast_category *cat, const char *name)
 	ast_copy_string(cat->name, name, sizeof(cat->name));
 }
 
-void ast_category_inherit(struct ast_category *new, const struct ast_category *base)
+int ast_category_inherit(struct ast_category *new, const struct ast_category *base)
 {
 	struct ast_variable *var;
 	struct ast_category_template_instance *x;
 
 	x = ast_calloc(1, sizeof(*x));
 	if (!x) {
-		return;
+		return -1;
 	}
 	strcpy(x->name, base->name);
 	x->inst = base;
 	AST_LIST_INSERT_TAIL(&new->template_instances, x, next);
-	for (var = base->root; var; var = var->next)
-		ast_variable_append(new, variable_clone(var));
+	for (var = base->root; var; var = var->next) {
+		struct ast_variable *cloned = variable_clone(var);
+		if (!cloned) {
+			return -1;
+		}
+		cloned->inherited = 1;
+		ast_variable_append(new, cloned);
+	}
+	return 0;
 }
 
 struct ast_config *ast_config_new(void)
@@ -1627,7 +1647,7 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
 		 *		You can put a comma-separated list of categories and templates
 		 *		and '!' and '+' between parentheses, with obvious meaning.
 		 */
-		struct ast_category *newcat = NULL;
+		struct ast_category *newcat;
 		char *catname;
 
 		c = strchr(cur, ']');
@@ -1640,14 +1660,13 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
 		if (*c++ != '(')
 			c = NULL;
 		catname = cur;
-		if (!(*cat = newcat = ast_category_new(catname,
-				S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile),
-				lineno))) {
+		*cat = newcat = ast_category_new(catname,
+			S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile),
+			lineno);
+		if (!newcat) {
 			return -1;
 		}
 		(*cat)->lineno = lineno;
-		*last_var = 0;
-		*last_cat = newcat;
 
 		/* add comments */
 		if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
@@ -1660,6 +1679,7 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
 		/* If there are options or categories to inherit from, process them now */
 		if (c) {
 			if (!(cur = strchr(c, ')'))) {
+				ast_category_destroy(newcat);
 				ast_log(LOG_WARNING, "parse error: no closing ')', line %d of %s\n", lineno, configfile);
 				return -1;
 			}
@@ -1670,12 +1690,15 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
 				} else if (!strcasecmp(cur, "+")) {
 					*cat = ast_category_get(cfg, catname, NULL);
 					if (!(*cat)) {
-						if (newcat)
+						if (newcat) {
 							ast_category_destroy(newcat);
+						}
 						ast_log(LOG_WARNING, "Category addition requested, but category '%s' does not exist, line %d of %s\n", catname, lineno, configfile);
 						return -1;
 					}
 					if (newcat) {
+						ast_config_set_current_category(cfg, *cat);
+						(*cat)->ignored |= newcat->ignored;
 						move_variables(newcat, *cat);
 						ast_category_destroy(newcat);
 						newcat = NULL;
@@ -1685,15 +1708,35 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
 
 					base = ast_category_get(cfg, cur, "TEMPLATES=include");
 					if (!base) {
+						if (newcat) {
+							ast_category_destroy(newcat);
+						}
 						ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile);
 						return -1;
 					}
-					ast_category_inherit(*cat, base);
+					if (ast_category_inherit(*cat, base)) {
+						if (newcat) {
+							ast_category_destroy(newcat);
+						}
+						ast_log(LOG_ERROR, "Inheritence requested, but allocation failed\n");
+						return -1;
+					}
 				}
 			}
 		}
-		if (newcat)
-			ast_category_append(cfg, *cat);
+
+		/*
+		 * We need to set *last_cat to newcat here regardless.  If the
+		 * category is being appended to we have no place for trailing
+		 * comments on the appended category.  The appended category
+		 * may be in another file or it already has trailing comments
+		 * that we would then leak.
+		 */
+		*last_var = NULL;
+		*last_cat = newcat;
+		if (newcat) {
+			ast_category_append(cfg, newcat);
+		}
 	} else if (cur[0] == '#') { /* A directive - #include or #exec */
 		char *cur2;
 		char real_inclusion_name[256];
@@ -1849,7 +1892,7 @@ set_new_variable:
 			} else if ((v = ast_variable_new(cur, ast_strip(c), S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile)))) {
 				v->lineno = lineno;
 				v->object = object;
-				*last_cat = 0;
+				*last_cat = NULL;
 				*last_var = v;
 				/* Put and reset comments */
 				v->blanklines = 0;
@@ -1889,8 +1932,8 @@ static struct ast_config *config_text_file_load(const char *database, const char
 	struct stat statbuf;
 	struct cache_file_mtime *cfmtime = NULL;
 	struct cache_file_include *cfinclude;
-	struct ast_variable *last_var = 0;
-	struct ast_category *last_cat = 0;
+	struct ast_variable *last_var = NULL;
+	struct ast_category *last_cat = NULL;
 	/*! Growable string buffer */
 	struct ast_str *comment_buffer = NULL;	/*!< this will be a comment collector.*/
 	struct ast_str *lline_buffer = NULL;	/*!< A buffer for stuff behind the ; */
@@ -2358,11 +2401,16 @@ static void insert_leading_blank_lines(FILE *fp, struct inclfile *fi, struct ast
 
 int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
 {
-	return ast_config_text_file_save(configfile, cfg, generator);
+	return ast_config_text_file_save2(configfile, cfg, generator, CONFIG_SAVE_FLAG_PRESERVE_EFFECTIVE_CONTEXT);
 }
 
 int ast_config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
 {
+	return ast_config_text_file_save2(configfile, cfg, generator, CONFIG_SAVE_FLAG_PRESERVE_EFFECTIVE_CONTEXT);
+}
+
+int ast_config_text_file_save2(const char *configfile, const struct ast_config *cfg, const char *generator, uint32_t flags)
+{
 	FILE *f;
 	char fn[PATH_MAX];
 	struct ast_variable *var;
@@ -2522,13 +2570,27 @@ int ast_config_text_file_save(const char *configfile, const struct ast_config *c
 				AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
 					struct ast_variable *v;
 					for (v = x->inst->root; v; v = v->next) {
-						if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
-							found = 1;
-							break;
+
+						if (flags & CONFIG_SAVE_FLAG_PRESERVE_EFFECTIVE_CONTEXT) {
+							if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
+								found = 1;
+								break;
+							}
+						} else {
+							if (var->inherited) {
+								found = 1;
+								break;
+							} else {
+								if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
+									found = 1;
+									break;
+								}
+							}
 						}
 					}
-					if (found)
+					if (found) {
 						break;
+					}
 				}
 				if (found) {
 					var = var->next;
@@ -2827,6 +2889,7 @@ int ast_realtime_is_mapping_defined(const char *family)
 			return 1;
 		}
 	}
+	ast_debug(5, "Failed to find a realtime mapping for %s\n", family);
 
 	return 0;
 }
@@ -3329,7 +3392,7 @@ int ast_update2_realtime(const char *family, ...)
 	va_end(ap);
 
 	va_start(ap, family);
-	realtime_arguments_to_fields2(ap, 1, &lookup_fields);
+	realtime_arguments_to_fields2(ap, 1, &update_fields);
 	va_end(ap);
 
 	if (!lookup_fields || !update_fields) {
@@ -3799,12 +3862,15 @@ static void config_shutdown(void)
 	AST_LIST_UNLOCK(&cfmtime_head);
 
 	ast_cli_unregister_multiple(cli_config, ARRAY_LEN(cli_config));
+
+	ao2_cleanup(cfg_hooks);
+	cfg_hooks = NULL;
 }
 
 int register_config_cli(void)
 {
 	ast_cli_register_multiple(cli_config, ARRAY_LEN(cli_config));
-	ast_register_atexit(config_shutdown);
+	ast_register_cleanup(config_shutdown);
 	return 0;
 }
 
@@ -3887,5 +3953,6 @@ int ast_config_hook_register(const char *name,
 	hook->module = ast_strdup(module);
 
 	ao2_link(cfg_hooks, hook);
+	ao2_ref(hook, -1);
 	return 0;
 }
diff --git a/main/config_options.c b/main/config_options.c
index 6ca8eae..0c706ac 100644
--- a/main/config_options.c
+++ b/main/config_options.c
@@ -27,7 +27,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419342 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <regex.h>
 
@@ -837,6 +837,10 @@ int aco_set_defaults(struct aco_type *type, const char *category, void *obj)
 	struct aco_option *opt;
 	struct ao2_iterator iter;
 
+	if (!type->internal) {
+		return -1;
+	}
+
 	iter = ao2_iterator_init(type->internal->opts, 0);
 
 	while ((opt = ao2_iterator_next(&iter))) {
@@ -1281,7 +1285,7 @@ static void aco_deinit(void)
 int aco_init(void)
 {
 #ifdef AST_XML_DOCS
-	ast_register_atexit(aco_deinit);
+	ast_register_cleanup(aco_deinit);
 	if (!(xmldocs = ast_xmldoc_build_documentation("configInfo"))) {
 		ast_log(LOG_ERROR, "Couldn't build config documentation\n");
 		return -1;
diff --git a/main/core_local.c b/main/core_local.c
index c79d88e..10bd839 100644
--- a/main/core_local.c
+++ b/main/core_local.c
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420124 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 /* ------------------------------------------------------------------- */
 
@@ -1001,22 +1001,11 @@ static int locals_cmp_cb(void *obj, void *arg, int flags)
  */
 static void local_shutdown(void)
 {
-	struct local_pvt *p;
-	struct ao2_iterator it;
-
 	/* First, take us out of the channel loop */
 	ast_cli_unregister_multiple(cli_local, ARRAY_LEN(cli_local));
 	ast_manager_unregister("LocalOptimizeAway");
 	ast_channel_unregister(&local_tech);
 
-	it = ao2_iterator_init(locals, 0);
-	while ((p = ao2_iterator_next(&it))) {
-		if (p->base.owner) {
-			ast_softhangup(p->base.owner, AST_SOFTHANGUP_APPUNLOAD);
-		}
-		ao2_ref(p, -1);
-	}
-	ao2_iterator_destroy(&it);
 	ao2_ref(locals, -1);
 	locals = NULL;
 
@@ -1066,6 +1055,6 @@ int ast_local_init(void)
 	ast_cli_register_multiple(cli_local, ARRAY_LEN(cli_local));
 	ast_manager_register_xml_core("LocalOptimizeAway", EVENT_FLAG_SYSTEM|EVENT_FLAG_CALL, manager_optimize_away);
 
-	ast_register_atexit(local_shutdown);
+	ast_register_cleanup(local_shutdown);
 	return 0;
 }
diff --git a/main/core_unreal.c b/main/core_unreal.c
index d93cbf0..d57e01f 100644
--- a/main/core_unreal.c
+++ b/main/core_unreal.c
@@ -32,7 +32,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 425783 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/causes.h"
 #include "asterisk/channel.h"
@@ -554,6 +554,18 @@ int ast_unreal_indicate(struct ast_channel *ast, int condition, const void *data
 		}
 		res = unreal_queue_indicate(p, ast, condition, data, datalen);
 		break;
+	case AST_CONTROL_RINGING:
+		/* Don't queue ringing frames if the channel is not in a "ring" state. Otherwise,
+		 * the real channel on the other end will likely start a playtones generator. It is
+		 * possible that this playtones generator will never be stopped under certain
+		 * circumstances.
+		 */
+		if (ast_channel_state(ast) == AST_STATE_RING) {
+			res = unreal_queue_indicate(p, ast, condition, data, datalen);
+		} else {
+			res = -1;
+		}
+		break;
 	default:
 		res = unreal_queue_indicate(p, ast, condition, data, datalen);
 		break;
diff --git a/main/crypt.c b/main/crypt.c
index a5c74b9..5e5df02 100644
--- a/main/crypt.c
+++ b/main/crypt.c
@@ -28,7 +28,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 393675 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <unistd.h>
 #if defined(HAVE_CRYPT_R)
diff --git a/main/data.c b/main/data.c
index d4e38aa..33a7c04 100644
--- a/main/data.c
+++ b/main/data.c
@@ -28,7 +28,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 424752 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/_private.h"
 
@@ -3342,7 +3342,7 @@ int ast_data_init(void)
 
 	AST_TEST_REGISTER(test_data_get);
 
-	ast_register_atexit(data_shutdown);
+	ast_register_cleanup(data_shutdown);
 
 	return res;
 }
diff --git a/main/datastore.c b/main/datastore.c
index 4d1de16..ed9b321 100644
--- a/main/datastore.c
+++ b/main/datastore.c
@@ -25,7 +25,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 393239 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/_private.h"
 
diff --git a/main/db.c b/main/db.c
index 93f94ed..4bb9355 100644
--- a/main/db.c
+++ b/main/db.c
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419504 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/_private.h"
 #include "asterisk/paths.h"	/* use ast_config_AST_DB */
@@ -841,7 +841,7 @@ static int manager_dbput(struct mansession *s, const struct message *m)
 static int manager_dbget(struct mansession *s, const struct message *m)
 {
 	const char *id = astman_get_header(m,"ActionID");
-	char idText[256] = "";
+	char idText[256];
 	const char *family = astman_get_header(m, "Family");
 	const char *key = astman_get_header(m, "Key");
 	char tmp[MAX_DB_FIELD];
@@ -856,6 +856,7 @@ static int manager_dbget(struct mansession *s, const struct message *m)
 		return 0;
 	}
 
+	idText[0] = '\0';
 	if (!ast_strlen_zero(id))
 		snprintf(idText, sizeof(idText) ,"ActionID: %s\r\n", id);
 
@@ -863,7 +864,8 @@ static int manager_dbget(struct mansession *s, const struct message *m)
 	if (res) {
 		astman_send_error(s, m, "Database entry not found");
 	} else {
-		astman_send_ack(s, m, "Result will follow");
+		astman_send_listack(s, m, "Result will follow", "start");
+
 		astman_append(s, "Event: DBGetResponse\r\n"
 				"Family: %s\r\n"
 				"Key: %s\r\n"
@@ -871,10 +873,9 @@ static int manager_dbget(struct mansession *s, const struct message *m)
 				"%s"
 				"\r\n",
 				family, key, tmp, idText);
-		astman_append(s, "Event: DBGetComplete\r\n"
-				"%s"
-				"\r\n",
-				idText);
+
+		astman_send_list_complete_start(s, m, "DBGetComplete", 1);
+		astman_send_list_complete_end(s);
 	}
 	return 0;
 }
diff --git a/main/devicestate.c b/main/devicestate.c
index e18b2ec..2983ee9 100644
--- a/main/devicestate.c
+++ b/main/devicestate.c
@@ -143,7 +143,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 422661 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/_private.h"
 #include "asterisk/channel.h"
@@ -397,6 +397,7 @@ enum ast_device_state ast_device_state(const char *device)
 /*! \brief Add device state provider */
 int ast_devstate_prov_add(const char *label, ast_devstate_prov_cb_type callback)
 {
+	struct devstate_prov *devcb;
 	struct devstate_prov *devprov;
 
 	if (!callback || !(devprov = ast_calloc(1, sizeof(*devprov))))
@@ -406,6 +407,14 @@ int ast_devstate_prov_add(const char *label, ast_devstate_prov_cb_type callback)
 	ast_copy_string(devprov->label, label, sizeof(devprov->label));
 
 	AST_RWLIST_WRLOCK(&devstate_provs);
+	AST_RWLIST_TRAVERSE(&devstate_provs, devcb, list) {
+		if (!strcasecmp(devcb->label, label)) {
+			ast_log(LOG_WARNING, "Device state provider '%s' already registered\n", label);
+			ast_free(devprov);
+			AST_RWLIST_UNLOCK(&devstate_provs);
+			return -1;
+		}
+	}
 	AST_RWLIST_INSERT_HEAD(&devstate_provs, devprov, list);
 	AST_RWLIST_UNLOCK(&devstate_provs);
 
diff --git a/main/dial.c b/main/dial.c
index d867ea8..fe59203 100644
--- a/main/dial.c
+++ b/main/dial.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 422684 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/time.h>
 #include <signal.h>
@@ -44,6 +44,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 422684 $")
 #include "asterisk/app.h"
 #include "asterisk/causes.h"
 #include "asterisk/stasis_channels.h"
+#include "asterisk/max_forwards.h"
 
 /*! \brief Main dialing structure. Contains global options, channels being dialed, and more! */
 struct ast_dial {
@@ -206,6 +207,7 @@ static const struct ast_option_types option_types[] = {
 	{ AST_DIAL_OPTION_DISABLE_CALL_FORWARDING, NULL, NULL },                  /*!< Disable call forwarding on channels */
 	{ AST_DIAL_OPTION_PREDIAL, predial_enable, predial_disable },             /*!< Execute a subroutine on the outbound channels prior to dialing */
 	{ AST_DIAL_OPTION_DIAL_REPLACES_SELF, NULL, NULL },                       /*!< The dial operation is a replacement for the requester */
+	{ AST_DIAL_OPTION_SELF_DESTROY, NULL, NULL},                              /*!< Destroy self at end of ast_dial_run */
 	{ AST_DIAL_OPTION_MAX, NULL, NULL },                                      /*!< Terminator of list */
 };
 
@@ -293,18 +295,33 @@ static int begin_dial_prerun(struct ast_dial_channel *channel, struct ast_channe
 	char numsubst[AST_MAX_EXTENSION];
 	struct ast_format_cap *cap_all_audio = NULL;
 	struct ast_format_cap *cap_request;
+	struct ast_format_cap *requester_cap = NULL;
 	struct ast_assigned_ids assignedids = {
 		.uniqueid = channel->assignedid1,
 		.uniqueid2 = channel->assignedid2,
 	};
 
+	if (chan) {
+		int max_forwards;
+
+		ast_channel_lock(chan);
+		max_forwards = ast_max_forwards_get(chan);
+		requester_cap = ao2_bump(ast_channel_nativeformats(chan));
+		ast_channel_unlock(chan);
+
+		if (max_forwards <= 0) {
+			ast_log(LOG_WARNING, "Cannot dial from channel '%s'. Max forwards exceeded\n",
+					ast_channel_name(chan));
+		}
+	}
+
 	/* Copy device string over */
 	ast_copy_string(numsubst, channel->device, sizeof(numsubst));
 
 	if (cap && ast_format_cap_count(cap)) {
 		cap_request = cap;
-	} else if (chan) {
-		cap_request = ast_channel_nativeformats(chan);
+	} else if (requester_cap) {
+		cap_request = requester_cap;
 	} else {
 		cap_all_audio = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
 		ast_format_cap_append_by_type(cap_all_audio, AST_MEDIA_TYPE_AUDIO);
@@ -317,6 +334,7 @@ static int begin_dial_prerun(struct ast_dial_channel *channel, struct ast_channe
 		return -1;
 	}
 	cap_request = NULL;
+	ao2_cleanup(requester_cap);
 	ao2_cleanup(cap_all_audio);
 
 	if (chan) {
@@ -336,6 +354,7 @@ static int begin_dial_prerun(struct ast_dial_channel *channel, struct ast_channe
 	if (chan) {
 		ast_channel_inherit_variables(chan, channel->owner);
 		ast_channel_datastore_inherit(chan, channel->owner);
+		ast_max_forwards_decrement(channel->owner);
 
 		/* Copy over callerid information */
 		ast_party_redirecting_copy(ast_channel_redirecting(channel->owner), ast_channel_redirecting(chan));
@@ -362,14 +381,13 @@ static int begin_dial_prerun(struct ast_dial_channel *channel, struct ast_channe
 	ast_channel_unlock(channel->owner);
 
 	if (!ast_strlen_zero(predial_string)) {
-		const char *predial_callee = ast_app_expand_sub_args(chan, predial_string);
-		if (!predial_callee) {
-			ast_log(LOG_ERROR, "Could not expand subroutine arguments in predial request '%s'\n", predial_string);
+		if (chan) {
+			ast_autoservice_start(chan);
+		}
+		ast_pre_call(channel->owner, predial_string);
+		if (chan) {
+			ast_autoservice_stop(chan);
 		}
-		ast_autoservice_start(chan);
-		ast_pre_call(channel->owner, predial_callee);
-		ast_autoservice_stop(chan);
-		ast_free((char *) predial_callee);
 	}
 
 	return 0;
@@ -381,10 +399,6 @@ int ast_dial_prerun(struct ast_dial *dial, struct ast_channel *chan, struct ast_
 	int res = -1;
 	char *predial_string = dial->options[AST_DIAL_OPTION_PREDIAL];
 
-	if (!ast_strlen_zero(predial_string)) {
-		ast_replace_subargument_delimiter(predial_string);
-	}
-
 	AST_LIST_LOCK(&dial->channels);
 	AST_LIST_TRAVERSE(&dial->channels, channel, list) {
 		if ((res = begin_dial_prerun(channel, chan, cap, predial_string))) {
@@ -434,10 +448,6 @@ static int begin_dial(struct ast_dial *dial, struct ast_channel *chan, int async
 	int success = 0;
 	char *predial_string = dial->options[AST_DIAL_OPTION_PREDIAL];
 
-	if (!ast_strlen_zero(predial_string)) {
-		ast_replace_subargument_delimiter(predial_string);
-	}
-
 	/* Iterate through channel list, requesting and calling each one */
 	AST_LIST_LOCK(&dial->channels);
 	AST_LIST_TRAVERSE(&dial->channels, channel, list) {
@@ -457,10 +467,6 @@ static int handle_call_forward(struct ast_dial *dial, struct ast_dial_channel *c
 	char *tech = "Local", *device = tmp, *stuff;
 	char *predial_string = dial->options[AST_DIAL_OPTION_PREDIAL];
 
-	if (!ast_strlen_zero(predial_string)) {
-		ast_replace_subargument_delimiter(predial_string);
-	}
-
 	/* If call forwarding is disabled just drop the original channel and don't attempt to dial the new one */
 	if (FIND_RELATIVE_OPTION(dial, channel, AST_DIAL_OPTION_DISABLE_CALL_FORWARDING)) {
 		ast_hangup(original);
@@ -889,6 +895,13 @@ static enum ast_dial_result monitor_dial(struct ast_dial *dial, struct ast_chann
 		AST_LIST_UNLOCK(&dial->channels);
 	}
 
+	if (dial->options[AST_DIAL_OPTION_SELF_DESTROY]) {
+		enum ast_dial_result state = dial->state;
+
+		ast_dial_destroy(dial);
+		return state;
+	}
+
 	return dial->state;
 }
 
diff --git a/main/dns.c b/main/dns.c
index 7092b40..320d917 100644
--- a/main/dns.c
+++ b/main/dns.c
@@ -35,7 +35,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 421678 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/network.h"
 #include <arpa/nameser.h>	/* res_* functions */
diff --git a/main/dnsmgr.c b/main/dnsmgr.c
index 8404aae..b5bfd17 100644
--- a/main/dnsmgr.c
+++ b/main/dnsmgr.c
@@ -45,7 +45,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 389733 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/_private.h"
 #include <regex.h>
@@ -438,7 +438,7 @@ int dnsmgr_init(void)
 	ast_cli_register(&cli_status);
 	ast_cli_register(&cli_refresh);
 
-	ast_register_atexit(dnsmgr_shutdown);
+	ast_register_cleanup(dnsmgr_shutdown);
 
 	return do_reload(1);
 }
diff --git a/main/dsp.c b/main/dsp.c
index c2a9c9d..0874163 100644
--- a/main/dsp.c
+++ b/main/dsp.c
@@ -55,7 +55,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419044 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <math.h>
 
@@ -112,9 +112,11 @@ static struct progalias {
 	{ "uk", PROG_MODE_UK },
 };
 
+#define FREQ_ARRAY_SIZE 7
+
 static struct progress {
 	enum gsamp_size size;
-	int freqs[7];
+	int freqs[FREQ_ARRAY_SIZE];
 } modes[] = {
 	{ GSAMP_SIZE_NA, { 350, 440, 480, 620, 950, 1400, 1800 } },	/*!< North America */
 	{ GSAMP_SIZE_CR, { 425 } },					/*!< Costa Rica, Brazil */
@@ -339,16 +341,6 @@ static inline void goertzel_sample(goertzel_state_t *s, short sample)
 	}
 }
 
-static inline void goertzel_update(goertzel_state_t *s, short *samps, int count)
-{
-	int i;
-
-	for (i = 0; i < count; i++) {
-		goertzel_sample(s, samps[i]);
-	}
-}
-
-
 static inline float goertzel_result(goertzel_state_t *s)
 {
 	goertzel_result_t r;
@@ -399,7 +391,7 @@ struct ast_dsp {
 	struct ast_dsp_busy_pattern busy_cadence;
 	int historicnoise[DSP_HISTORY];
 	int historicsilence[DSP_HISTORY];
-	goertzel_state_t freqs[7];
+	goertzel_state_t freqs[FREQ_ARRAY_SIZE];
 	int freqcount;
 	int gsamps;
 	enum gsamp_size gsamp_size;
@@ -1046,6 +1038,8 @@ static int __ast_dsp_call_progress(struct ast_dsp *dsp, short *s, int len)
 	int pass;
 	int newstate = DSP_TONE_STATE_SILENCE;
 	int res = 0;
+	int freqcount = dsp->freqcount > FREQ_ARRAY_SIZE ? FREQ_ARRAY_SIZE : dsp->freqcount;
+
 	while (len) {
 		/* Take the lesser of the number of samples we need and what we have */
 		pass = len;
@@ -1055,7 +1049,7 @@ static int __ast_dsp_call_progress(struct ast_dsp *dsp, short *s, int len)
 		for (x = 0; x < pass; x++) {
 			samp = s[x];
 			dsp->genergy += (int32_t) samp * (int32_t) samp;
-			for (y = 0; y < dsp->freqcount; y++) {
+			for (y = 0; y < freqcount; y++) {
 				goertzel_sample(&dsp->freqs[y], samp);
 			}
 		}
@@ -1063,8 +1057,8 @@ static int __ast_dsp_call_progress(struct ast_dsp *dsp, short *s, int len)
 		dsp->gsamps += pass;
 		len -= pass;
 		if (dsp->gsamps == dsp->gsamp_size) {
-			float hz[7];
-			for (y = 0; y < 7; y++) {
+			float hz[FREQ_ARRAY_SIZE];
+			for (y = 0; y < FREQ_ARRAY_SIZE; y++) {
 				hz[y] = goertzel_result(&dsp->freqs[y]);
 			}
 			switch (dsp->progmode) {
@@ -1652,7 +1646,7 @@ static void ast_dsp_prog_reset(struct ast_dsp *dsp)
 
 	dsp->gsamp_size = modes[dsp->progmode].size;
 	dsp->gsamps = 0;
-	for (x = 0; x < ARRAY_LEN(modes[dsp->progmode].freqs); x++) {
+	for (x = 0; x < FREQ_ARRAY_SIZE; x++) {
 		if (modes[dsp->progmode].freqs[x]) {
 			goertzel_init(&dsp->freqs[x], (float)modes[dsp->progmode].freqs[x], dsp->sample_rate);
 			max = x + 1;
@@ -1678,6 +1672,7 @@ static struct ast_dsp *__ast_dsp_new(unsigned int sample_rate)
 		dsp->digitmode = DSP_DIGITMODE_DTMF;
 		dsp->faxmode = DSP_FAXMODE_DETECT_CNG;
 		dsp->sample_rate = sample_rate;
+		dsp->freqcount = 0;
 		/* Initialize digit detector */
 		ast_digit_detect_init(&dsp->digit_state, dsp->digitmode & DSP_DIGITMODE_MF, dsp->sample_rate);
 		dsp->display_inband_dtmf_warning = 1;
@@ -1707,6 +1702,13 @@ void ast_dsp_set_features(struct ast_dsp *dsp, int features)
 	}
 }
 
+
+int ast_dsp_get_features(struct ast_dsp *dsp)
+{
+        return (dsp->features);
+}
+
+
 void ast_dsp_free(struct ast_dsp *dsp)
 {
 	ast_free(dsp);
diff --git a/main/editline/np/strlcat.c b/main/editline/np/strlcat.c
index 6c9f1e9..d9d0e72 100644
--- a/main/editline/np/strlcat.c
+++ b/main/editline/np/strlcat.c
@@ -26,14 +26,6 @@
  */
 
 #include "config.h"
-#if defined(LIBC_SCCS) && !defined(lint)
-static char *rcsid = "$OpenBSD: strlcat.c,v 1.2 1999/06/17 16:28:58 millert Exp $";
-#endif /* LIBC_SCCS and not lint */
-#ifndef lint
-static const char rcsid[] =
-  "$FreeBSD: src/lib/libc/string/strlcat.c,v 1.2.4.2 2001/07/09 23:30:06 obrien Exp $";
-#endif
-
 #include <sys/types.h>
 #include <string.h>
 
diff --git a/main/editline/np/strlcpy.c b/main/editline/np/strlcpy.c
index 1f154bc..ecad62b 100644
--- a/main/editline/np/strlcpy.c
+++ b/main/editline/np/strlcpy.c
@@ -28,16 +28,6 @@
  */
 
 #include "config.h"
-#if defined(LIBC_SCCS) && !defined(lint)
-#if 0
-static char *rcsid = "$OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $";
-#endif
-#endif /* LIBC_SCCS and not lint */
-#ifndef lint
-static const char rcsid[] =
-  "$FreeBSD: src/lib/libc/string/strlcpy.c,v 1.2.4.1 2001/07/09 23:30:06 obrien Exp $";
-#endif
-
 #include <sys/types.h>
 #include <string.h>
 
diff --git a/main/endpoints.c b/main/endpoints.c
index 830bb5c..0155adf 100644
--- a/main/endpoints.c
+++ b/main/endpoints.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428687 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/astobj2.h"
 #include "asterisk/endpoints.h"
@@ -415,6 +415,14 @@ const char *ast_endpoint_get_id(const struct ast_endpoint *endpoint)
 	return endpoint->id;
 }
 
+enum ast_endpoint_state ast_endpoint_get_state(const struct ast_endpoint *endpoint)
+{
+	if (!endpoint) {
+		return AST_ENDPOINT_UNKNOWN;
+	}
+	return endpoint->state;
+}
+
 void ast_endpoint_set_state(struct ast_endpoint *endpoint,
 	enum ast_endpoint_state state)
 {
@@ -456,7 +464,7 @@ static void endpoint_snapshot_dtor(void *obj)
 struct ast_endpoint_snapshot *ast_endpoint_snapshot_create(
 	struct ast_endpoint *endpoint)
 {
-	RAII_VAR(struct ast_endpoint_snapshot *, snapshot, NULL, ao2_cleanup);
+	struct ast_endpoint_snapshot *snapshot;
 	int channel_count;
 	struct ao2_iterator i;
 	void *obj;
@@ -467,11 +475,13 @@ struct ast_endpoint_snapshot *ast_endpoint_snapshot_create(
 
 	channel_count = ao2_container_count(endpoint->channel_ids);
 
-	snapshot = ao2_alloc(
+	snapshot = ao2_alloc_options(
 		sizeof(*snapshot) + channel_count * sizeof(char *),
-		endpoint_snapshot_dtor);
+		endpoint_snapshot_dtor,
+		AO2_ALLOC_OPT_LOCK_NOLOCK);
 
-	if (ast_string_field_init(snapshot, 80) != 0) {
+	if (!snapshot || ast_string_field_init(snapshot, 80) != 0) {
+		ao2_cleanup(snapshot);
 		return NULL;
 	}
 
@@ -490,7 +500,6 @@ struct ast_endpoint_snapshot *ast_endpoint_snapshot_create(
 	}
 	ao2_iterator_destroy(&i);
 
-	ao2_ref(snapshot, +1);
 	return snapshot;
 }
 
diff --git a/main/enum.c b/main/enum.c
index 9bcdae2..bae1299 100644
--- a/main/enum.c
+++ b/main/enum.c
@@ -60,7 +60,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 413589 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/socket.h>
 #include <netinet/in.h>
@@ -257,7 +257,7 @@ struct ebl_context {
 static int ebl_callback(void *context, unsigned char *answer, int len, unsigned char *fullanswer)
 {
 	struct ebl_context *c = context;
-	unsigned int i;
+	int i;
 
 	c->pos = 0;	/* default to empty */
 	c->separator[0] = 0;
diff --git a/main/event.c b/main/event.c
index f274837..019dfca 100644
--- a/main/event.c
+++ b/main/event.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 417571 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/_private.h"
 
@@ -199,7 +199,7 @@ const char *ast_event_get_type_name(const struct ast_event *event)
 
 	type = ast_event_get_type(event);
 
-	if (type < 0 || type >= ARRAY_LEN(event_names)) {
+	if ((unsigned int)type >= ARRAY_LEN(event_names)) {
 		ast_log(LOG_ERROR, "Invalid event type - '%u'\n", type);
 		return "";
 	}
diff --git a/main/features.c b/main/features.c
index ebb79b0..1810b15 100644
--- a/main/features.c
+++ b/main/features.c
@@ -39,7 +39,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 427737 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/_private.h"
 
@@ -66,7 +66,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 427737 $")
 #include "asterisk/utils.h"
 #include "asterisk/adsi.h"
 #include "asterisk/devicestate.h"
-#include "asterisk/monitor.h"
 #include "asterisk/audiohook.h"
 #include "asterisk/global_datastores.h"
 #include "asterisk/astobj2.h"
@@ -78,6 +77,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 427737 $")
 #include "asterisk/stasis.h"
 #include "asterisk/stasis_channels.h"
 #include "asterisk/features_config.h"
+#include "asterisk/max_forwards.h"
 
 /*** DOCUMENTATION
 	<application name="Bridge" language="en_US">
@@ -318,11 +318,6 @@ struct ast_bridge_thread_obj
 	unsigned int return_to_pbx:1;
 };
 
-static const struct ast_datastore_info channel_app_data_datastore = {
-	.type = "Channel appdata datastore",
-	.destroy = ast_free_ptr,
-};
-
 static void set_config_flags(struct ast_channel *chan, struct ast_bridge_config *config)
 {
 	ast_clear_flag(config, AST_FLAGS_ALL);
@@ -426,22 +421,6 @@ static void add_features_datastores(struct ast_channel *caller, struct ast_chann
 	add_features_datastore(callee, &config->features_callee, &config->features_caller);
 }
 
-static void clear_dialed_interfaces(struct ast_channel *chan)
-{
-	struct ast_datastore *di_datastore;
-
-	ast_channel_lock(chan);
-	if ((di_datastore = ast_channel_datastore_find(chan, &dialed_interface_info, NULL))) {
-		if (option_debug) {
-			ast_log(LOG_DEBUG, "Removing dialed interfaces datastore on %s since we're bridging\n", ast_channel_name(chan));
-		}
-		if (!ast_channel_datastore_remove(chan, di_datastore)) {
-			ast_datastore_free(di_datastore);
-		}
-	}
-	ast_channel_unlock(chan);
-}
-
 static void bridge_config_set_limits_warning_values(struct ast_bridge_config *config, struct ast_bridge_features_limits *limits)
 {
 	if (config->end_sound) {
@@ -578,20 +557,13 @@ static int pre_bridge_setup(struct ast_channel *chan, struct ast_channel *peer,
 	ast_channel_log("Pre-bridge PEER Channel info", peer);
 #endif
 
-	/*
-	 * If we are bridging a call, stop worrying about forwarding
-	 * loops.  We presume that if a call is being bridged, that the
-	 * humans in charge know what they're doing.  If they don't,
-	 * well, what can we do about that?
-	 */
-	clear_dialed_interfaces(chan);
-	clear_dialed_interfaces(peer);
-
 	res = 0;
 	ast_channel_lock(chan);
+	ast_max_forwards_reset(chan);
 	res |= ast_bridge_features_ds_append(chan, &config->features_caller);
 	ast_channel_unlock(chan);
 	ast_channel_lock(peer);
+	ast_max_forwards_reset(peer);
 	res |= ast_bridge_features_ds_append(peer, &config->features_callee);
 	ast_channel_unlock(peer);
 
@@ -1099,7 +1071,7 @@ static int bridge_exec(struct ast_channel *chan, const char *data)
 		extension = ast_strdupa(ast_channel_exten(current_dest_chan));
 		priority = ast_channel_priority(current_dest_chan);
 		ast_channel_unlock(current_dest_chan);
-		ast_bridge_set_after_goto(current_dest_chan, context, extension, priority);
+		ast_bridge_set_after_go_on(current_dest_chan, context, extension, priority, NULL);
 	}
 
 	if (ast_bridge_features_init(&chan_features)) {
@@ -1185,7 +1157,7 @@ int ast_features_init(void)
 	if (res) {
 		features_shutdown();
 	} else {
-		ast_register_atexit(features_shutdown);
+		ast_register_cleanup(features_shutdown);
 	}
 
 	return res;
diff --git a/main/file.c b/main/file.c
index 7aeb32a..7ce0213 100644
--- a/main/file.c
+++ b/main/file.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 427466 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <dirent.h>
 #include <sys/stat.h>
@@ -902,7 +902,7 @@ static enum fsread_res ast_readaudio_callback(struct ast_filestream *s)
 
 		if (!fr /* stream complete */ || ast_write(s->owner, fr) /* error writing */) {
 			if (fr) {
-				ast_log(LOG_WARNING, "Failed to write frame\n");
+				ast_debug(2, "Failed to write frame\n");
 				ast_frfree(fr);
 			}
 			goto return_failure;
@@ -959,7 +959,7 @@ static enum fsread_res ast_readvideo_callback(struct ast_filestream *s)
 
 		if (!fr /* stream complete */ || ast_write(s->owner, fr) /* error writing */) {
 			if (fr) {
-				ast_log(LOG_WARNING, "Failed to write frame\n");
+				ast_debug(2, "Failed to write frame\n");
 				ast_frfree(fr);
 			}
 			ast_channel_vstreamid_set(s->owner, -1);
@@ -1096,7 +1096,7 @@ int ast_streamfile(struct ast_channel *chan, const char *filename, const char *p
 
 	fs = ast_openstream(chan, filename, preflang);
 	if (!fs) {
-		struct ast_str *codec_buf = ast_str_alloca(64);
+		struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 		ast_log(LOG_WARNING, "Unable to open %s (format %s): %s\n",
 			filename, ast_format_cap_get_names(ast_channel_nativeformats(chan), &codec_buf), strerror(errno));
 		return -1;
@@ -1785,6 +1785,6 @@ int ast_file_init(void)
 	STASIS_MESSAGE_TYPE_INIT(ast_format_register_type);
 	STASIS_MESSAGE_TYPE_INIT(ast_format_unregister_type);
 	ast_cli_register_multiple(cli_file, ARRAY_LEN(cli_file));
-	ast_register_atexit(file_shutdown);
+	ast_register_cleanup(file_shutdown);
 	return 0;
 }
diff --git a/main/fixedjitterbuf.c b/main/fixedjitterbuf.c
index 869c8d3..17cad49 100644
--- a/main/fixedjitterbuf.c
+++ b/main/fixedjitterbuf.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 369013 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <assert.h>
 
diff --git a/main/format.c b/main/format.c
index 7b4af84..5bf38df 100644
--- a/main/format.c
+++ b/main/format.c
@@ -29,13 +29,14 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420028 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/logger.h"
 #include "asterisk/codec.h"
 #include "asterisk/format.h"
 #include "asterisk/astobj2.h"
 #include "asterisk/strings.h"
+#include "asterisk/module.h"
 
 /*! \brief Number of buckets to use for format interfaces (should be prime for performance reasons) */
 #define FORMAT_INTERFACE_BUCKETS 53
@@ -126,7 +127,7 @@ int ast_format_init(void)
 		return -1;
 	}
 
-	ast_register_atexit(format_shutdown);
+	ast_register_cleanup(format_shutdown);
 
 	return 0;
 }
@@ -156,6 +157,8 @@ int __ast_format_interface_register(const char *codec, const struct ast_format_i
 	format_interface->interface = interface;
 	strcpy(format_interface->codec, codec); /* Safe */
 
+	/* Once registered a format interface cannot be unregistered. */
+	ast_module_shutdown_ref(mod);
 	ao2_link_flags(interfaces, format_interface, OBJ_NOLOCK);
 	ao2_ref(format_interface, -1);
 
@@ -295,6 +298,25 @@ struct ast_format *ast_format_attribute_set(const struct ast_format *format, con
 	return interface->format_attribute_set(format, name, value);
 }
 
+const void *ast_format_attribute_get(const struct ast_format *format, const char *name)
+{
+	const struct ast_format_interface *interface = format->interface;
+
+	if (!interface) {
+		struct format_interface *format_interface = ao2_find(interfaces, format->codec->name, OBJ_SEARCH_KEY);
+		if (format_interface) {
+			interface = format_interface->interface;
+			ao2_ref(format_interface, -1);
+		}
+	}
+
+	if (!interface || !interface->format_attribute_get) {
+		return NULL;
+	}
+
+	return interface->format_attribute_get(format, name);
+}
+
 struct ast_format *ast_format_parse_sdp_fmtp(const struct ast_format *format, const char *attributes)
 {
 	const struct ast_format_interface *interface = format->interface;
@@ -316,11 +338,26 @@ struct ast_format *ast_format_parse_sdp_fmtp(const struct ast_format *format, co
 
 void ast_format_generate_sdp_fmtp(const struct ast_format *format, unsigned int payload, struct ast_str **str)
 {
-	if (!format->interface || !format->interface->format_generate_sdp_fmtp) {
+	const struct ast_format_interface *interface = format->interface;
+
+	if (!interface) {
+		struct format_interface *format_interface = ao2_find(interfaces, format->codec->name, OBJ_SEARCH_KEY);
+		if (format_interface) {
+			interface = format_interface->interface;
+			ao2_ref(format_interface, -1);
+		}
+	}
+
+	if (!interface || !interface->format_generate_sdp_fmtp) {
 		return;
 	}
 
-	format->interface->format_generate_sdp_fmtp(format, payload, str);
+	interface->format_generate_sdp_fmtp(format, payload, str);
+}
+
+struct ast_codec *ast_format_get_codec(const struct ast_format *format)
+{
+	return ao2_bump(format->codec);
 }
 
 unsigned int ast_format_get_codec_id(const struct ast_format *format)
@@ -370,7 +407,7 @@ unsigned int ast_format_get_minimum_bytes(const struct ast_format *format)
 
 unsigned int ast_format_get_sample_rate(const struct ast_format *format)
 {
-	return format->codec->sample_rate;
+	return format->codec->sample_rate ?: 8000;
 }
 
 unsigned int ast_format_determine_length(const struct ast_format *format, unsigned int samples)
diff --git a/main/format_cache.c b/main/format_cache.c
index b86ff20..6638a78 100644
--- a/main/format_cache.c
+++ b/main/format_cache.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 421678 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/logger.h"
 #include "asterisk/format.h"
@@ -341,7 +341,7 @@ int ast_format_cache_init(void)
 		return -1;
 	}
 
-	ast_register_atexit(format_cache_shutdown);
+	ast_register_cleanup(format_cache_shutdown);
 
 	return 0;
 }
@@ -500,6 +500,7 @@ struct ast_format *ast_format_cache_get_slin_by_rate(unsigned int rate)
 int ast_format_cache_is_slinear(struct ast_format *format)
 {
 	if ((ast_format_cmp(format, ast_format_slin) == AST_FORMAT_CMP_EQUAL)
+		|| (ast_format_cmp(format, ast_format_slin12) == AST_FORMAT_CMP_EQUAL)
 		|| (ast_format_cmp(format, ast_format_slin16) == AST_FORMAT_CMP_EQUAL)
 		|| (ast_format_cmp(format, ast_format_slin24) == AST_FORMAT_CMP_EQUAL)
 		|| (ast_format_cmp(format, ast_format_slin32) == AST_FORMAT_CMP_EQUAL)
diff --git a/main/format_cap.c b/main/format_cap.c
index a16b14d..17ae18c 100644
--- a/main/format_cap.c
+++ b/main/format_cap.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 423414 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/logger.h"
 #include "asterisk/format.h"
@@ -93,14 +93,27 @@ static void format_cap_destroy(void *obj)
 	AST_VECTOR_FREE(&cap->preference_order);
 }
 
-static inline void format_cap_init(struct ast_format_cap *cap, enum ast_format_cap_flags flags)
+/*
+ * \brief Initialize values on an ast_format_cap
+ *
+ * \param cap ast_format_cap to initialize
+ * \param flags Unused.
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+static inline int format_cap_init(struct ast_format_cap *cap, enum ast_format_cap_flags flags)
 {
-	AST_VECTOR_INIT(&cap->formats, 0);
+	if (AST_VECTOR_INIT(&cap->formats, 0)) {
+		return -1;
+	}
 
 	/* TODO: Look at common usage of this and determine a good starting point */
-	AST_VECTOR_INIT(&cap->preference_order, 5);
+	if (AST_VECTOR_INIT(&cap->preference_order, 5)) {
+		return -1;
+	}
 
 	cap->framing = UINT_MAX;
+	return 0;
 }
 
 struct ast_format_cap *__ast_format_cap_alloc(enum ast_format_cap_flags flags)
@@ -112,7 +125,10 @@ struct ast_format_cap *__ast_format_cap_alloc(enum ast_format_cap_flags flags)
 		return NULL;
 	}
 
-	format_cap_init(cap, flags);
+	if (format_cap_init(cap, flags)) {
+		ao2_ref(cap, -1);
+		return NULL;
+	}
 
 	return cap;
 }
@@ -126,7 +142,10 @@ struct ast_format_cap *__ast_format_cap_alloc_debug(enum ast_format_cap_flags fl
 		return NULL;
 	}
 
-	format_cap_init(cap, flags);
+	if (format_cap_init(cap, flags)) {
+		ao2_ref(cap, -1);
+		return NULL;
+	}
 
 	return cap;
 }
@@ -151,7 +170,7 @@ static inline int format_cap_framed_init(struct format_cap_framed *framed, struc
 	framed->framing = framing;
 
 	if (ast_format_get_codec_id(format) >= AST_VECTOR_SIZE(&cap->formats)) {
-		if (AST_VECTOR_INSERT(&cap->formats, ast_format_get_codec_id(format), format_cap_framed_list_empty)) {
+		if (AST_VECTOR_REPLACE(&cap->formats, ast_format_get_codec_id(format), format_cap_framed_list_empty)) {
 			ao2_ref(framed, -1);
 			return -1;
 		}
@@ -233,6 +252,7 @@ int ast_format_cap_append_by_type(struct ast_format_cap *cap, enum ast_media_typ
 
 	for (id = 1; id < ast_codec_get_max(); ++id) {
 		struct ast_codec *codec = ast_codec_get_by_id(id);
+		struct ast_codec *codec2 = NULL;
 		struct ast_format *format;
 		int res;
 
@@ -245,7 +265,22 @@ int ast_format_cap_append_by_type(struct ast_format_cap *cap, enum ast_media_typ
 			continue;
 		}
 
-		format = ast_format_create(codec);
+		format = ast_format_cache_get(codec->name);
+
+		if (format == ast_format_none) {
+			ao2_ref(format, -1);
+			ao2_ref(codec, -1);
+			continue;
+		}
+
+		if (format) {
+			codec2 = ast_format_get_codec(format);
+		}
+		if (codec != codec2) {
+			ao2_cleanup(format);
+			format = ast_format_create(codec);
+		}
+		ao2_cleanup(codec2);
 		ao2_ref(codec, -1);
 
 		if (!format) {
@@ -319,8 +354,29 @@ int ast_format_cap_update_by_allow_disallow(struct ast_format_cap *cap, const ch
 	int res = 0, all = 0, iter_allowing;
 	char *parse = NULL, *this = NULL, *psize = NULL;
 
+	if (!allowing && ast_strlen_zero(list)) {
+		return 0;
+	}
+
 	parse = ast_strdupa(list);
-	while ((this = strsep(&parse, ","))) {
+
+	/* If the list is being fed to us as a result of ast_format_cap_get_names,
+	 * strip off the paranthesis and immediately apply the inverse of the
+	 * allowing option
+	 */
+	if (parse[0] == '(' && parse[strlen(parse) - 1] == ')') {
+		parse++;
+		parse[strlen(parse) - 1] = '\0';
+
+		if (allowing) {
+			ast_format_cap_remove_by_type(cap, AST_MEDIA_TYPE_UNKNOWN);
+		} else {
+			ast_format_cap_append_by_type(cap, AST_MEDIA_TYPE_UNKNOWN);
+		}
+	}
+
+
+	while ((this = strsep(&parse, ",|"))) {
 		int framems = 0;
 		struct ast_format *format = NULL;
 
diff --git a/main/format_compatibility.c b/main/format_compatibility.c
index 747f3aa..cf66af2 100644
--- a/main/format_compatibility.c
+++ b/main/format_compatibility.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420364 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/logger.h"
 #include "asterisk/astobj2.h"
diff --git a/main/frame.c b/main/frame.c
index e3a969c..64af882 100644
--- a/main/frame.c
+++ b/main/frame.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419044 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/_private.h"
 #include "asterisk/lock.h"
diff --git a/main/framehook.c b/main/framehook.c
index 54537c5..e42a232 100644
--- a/main/framehook.c
+++ b/main/framehook.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 424507 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/channel.h"
 #include "asterisk/linkedlists.h"
@@ -137,7 +137,7 @@ int ast_framehook_attach(struct ast_channel *chan, struct ast_framehook_interfac
 	struct ast_framehook_list *fh_list;
 	struct ast_frame *frame;
 	if (i->version != AST_FRAMEHOOK_INTERFACE_VERSION) {
-		ast_log(LOG_ERROR, "Version '%hu' of framehook interface not what we compiled against (%hu)\n",
+		ast_log(LOG_ERROR, "Version '%hu' of framehook interface not what we compiled against (%i)\n",
 			i->version, AST_FRAMEHOOK_INTERFACE_VERSION);
 		return -1;
 	}
diff --git a/main/fskmodem_float.c b/main/fskmodem_float.c
index 97ab895..7bddee7 100644
--- a/main/fskmodem_float.c
+++ b/main/fskmodem_float.c
@@ -34,7 +34,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 369013 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <stdio.h>
 
diff --git a/main/fskmodem_int.c b/main/fskmodem_int.c
index 3a7521a..cc720fe 100644
--- a/main/fskmodem_int.c
+++ b/main/fskmodem_int.c
@@ -34,7 +34,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 369013 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/fskmodem.h"
 
diff --git a/main/global_datastores.c b/main/global_datastores.c
index 93d299c..b63e1df 100644
--- a/main/global_datastores.c
+++ b/main/global_datastores.c
@@ -29,65 +29,9 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 369013 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/global_datastores.h"
-#include "asterisk/linkedlists.h"
-
-static void dialed_interface_destroy(void *data)
-{
-	struct ast_dialed_interface *di = NULL;
-	AST_LIST_HEAD(, ast_dialed_interface) *dialed_interface_list = data;
-	
-	if (!dialed_interface_list) {
-		return;
-	}
-
-	AST_LIST_LOCK(dialed_interface_list);
-	while ((di = AST_LIST_REMOVE_HEAD(dialed_interface_list, list)))
-		ast_free(di);
-	AST_LIST_UNLOCK(dialed_interface_list);
-
-	AST_LIST_HEAD_DESTROY(dialed_interface_list);
-	ast_free(dialed_interface_list);
-}
-
-static void *dialed_interface_duplicate(void *data)
-{
-	struct ast_dialed_interface *di = NULL;
-	AST_LIST_HEAD(, ast_dialed_interface) *old_list;
-	AST_LIST_HEAD(, ast_dialed_interface) *new_list = NULL;
-
-	if(!(old_list = data)) {
-		return NULL;
-	}
-
-	if(!(new_list = ast_calloc(1, sizeof(*new_list)))) {
-		return NULL;
-	}
-
-	AST_LIST_HEAD_INIT(new_list);
-	AST_LIST_LOCK(old_list);
-	AST_LIST_TRAVERSE(old_list, di, list) {
-		struct ast_dialed_interface *di2 = ast_calloc(1, sizeof(*di2) + strlen(di->interface));
-		if(!di2) {
-			AST_LIST_UNLOCK(old_list);
-			dialed_interface_destroy(new_list);
-			return NULL;
-		}
-		strcpy(di2->interface, di->interface);
-		AST_LIST_INSERT_TAIL(new_list, di2, list);
-	}
-	AST_LIST_UNLOCK(old_list);
-
-	return new_list;
-}
-
-const struct ast_datastore_info dialed_interface_info = {
-	.type = "dialed-interface",
-	.destroy = dialed_interface_destroy,
-	.duplicate = dialed_interface_duplicate,
-};
 
 static void secure_call_store_destroy(void *data)
 {
diff --git a/main/hashtab.c b/main/hashtab.c
index 09166dc..9b334d4 100644
--- a/main/hashtab.c
+++ b/main/hashtab.c
@@ -28,7 +28,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 396850 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <ctype.h>
 
@@ -43,10 +43,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 396850 $")
 #include "asterisk/hashtab.h"
 
 
-#ifndef __AST_DEBUG_MALLOC
-void *_ast_mem_backtrace_buffer[_AST_MEM_BACKTRACE_BUFLEN];
-#endif
-
 #if (defined(MALLOC_DEBUG) && !defined(STANDALONE))
 static void _ast_hashtab_resize(struct ast_hashtab *tab, const char *file, int lineno, const char *func);
 #define ast_hashtab_resize(a)	_ast_hashtab_resize(a,__FILE__, __LINE__, __PRETTY_FUNCTION__)
@@ -749,6 +745,8 @@ struct ast_hashtab_iter *ast_hashtab_start_write_traversal(struct ast_hashtab *t
 
 void ast_hashtab_end_traversal(struct ast_hashtab_iter *it)
 {
+	if (!it)
+		return;
 	if (it->tab->do_locking)
 		ast_rwlock_unlock(&it->tab->lock);
 	free(it);
diff --git a/main/heap.c b/main/heap.c
index e441790..c04f7a0 100644
--- a/main/heap.c
+++ b/main/heap.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 398760 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/heap.h"
 #include "asterisk/utils.h"
diff --git a/main/http.c b/main/http.c
index 2319448..c343cb2 100644
--- a/main/http.c
+++ b/main/http.c
@@ -44,7 +44,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 418067 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <time.h>
 #include <sys/time.h>
@@ -77,6 +77,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 418067 $")
 #define MIN_INITIAL_REQUEST_TIMEOUT	10000
 /*! (ms) Idle time between HTTP requests */
 #define DEFAULT_SESSION_KEEP_ALIVE 15000
+/*! Max size for the http server name */
+#define	MAX_SERVER_NAME_LENGTH 128
+/*! Max size for the http response header */
+#define	DEFAULT_RESPONSE_HEADER_LENGTH 512
 
 /*! Maximum application/json or application/x-www-form-urlencoded body content length. */
 #if !defined(LOW_MEMORY)
@@ -92,6 +96,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 418067 $")
 #define MAX_HTTP_LINE_LENGTH 1024
 #endif	/* !defined(LOW_MEMORY) */
 
+static char http_server_name[MAX_SERVER_NAME_LENGTH];
+
 static int session_limit = DEFAULT_SESSION_LIMIT;
 static int session_inactivity = DEFAULT_SESSION_INACTIVITY;
 static int session_keep_alive = DEFAULT_SESSION_KEEP_ALIVE;
@@ -370,11 +376,12 @@ static int httpstatus_callback(struct ast_tcptls_session_instance *ser,
 	}
 
 	ast_str_append(&out, 0,
-		"<title>Asterisk HTTP Status</title>\r\n"
+		"<html><title>Asterisk HTTP Status</title>\r\n"
 		"<body bgcolor=\"#ffffff\">\r\n"
 		"<table bgcolor=\"#f1f1f1\" align=\"center\"><tr><td bgcolor=\"#e0e0ff\" colspan=\"2\" width=\"500\">\r\n"
 		"<h2>  Asterisk™ HTTP Status</h2></td></tr>\r\n");
 
+	ast_str_append(&out, 0, "<tr><td><i>Server</i></td><td><b>%s</b></td></tr>\r\n", http_server_name);
 	ast_str_append(&out, 0, "<tr><td><i>Prefix</i></td><td><b>%s</b></td></tr>\r\n", prefix);
 	ast_str_append(&out, 0, "<tr><td><i>Bind Address</i></td><td><b>%s</b></td></tr>\r\n",
 		       ast_sockaddr_stringify_addr(&http_desc.old_address));
@@ -396,7 +403,7 @@ static int httpstatus_callback(struct ast_tcptls_session_instance *ser,
 	}
 	ast_variables_destroy(cookies);
 
-	ast_str_append(&out, 0, "</table><center><font size=\"-1\"><i>Asterisk and Digium are registered trademarks of Digium, Inc.</i></font></center></body>\r\n");
+	ast_str_append(&out, 0, "</table><center><font size=\"-1\"><i>Asterisk and Digium are registered trademarks of Digium, Inc.</i></font></center></body></html>\r\n");
 	ast_http_send(ser, method, 200, NULL, NULL, out, 0, 0);
 	return 0;
 }
@@ -446,14 +453,23 @@ void ast_http_send(struct ast_tcptls_session_instance *ser,
 	char timebuf[80];
 	int content_length = 0;
 	int close_connection;
+	struct ast_str *server_header_field = ast_str_create(MAX_SERVER_NAME_LENGTH);
 
-	if (!ser || !ser->f) {
+	if (!ser || !ser->f || !server_header_field) {
 		/* The connection is not open. */
 		ast_free(http_header);
 		ast_free(out);
+		ast_free(server_header_field);
 		return;
 	}
 
+	if(!ast_strlen_zero(http_server_name)) {
+		ast_str_set(&server_header_field,
+	                0,
+	                "Server: %s\r\n",
+	                http_server_name);
+	}
+
 	/*
 	 * We shouldn't be sending non-final status codes to this
 	 * function because we may close the connection before
@@ -491,7 +507,7 @@ void ast_http_send(struct ast_tcptls_session_instance *ser,
 	/* send http header */
 	fprintf(ser->f,
 		"HTTP/1.1 %d %s\r\n"
-		"Server: Asterisk/%s\r\n"
+		"%s"
 		"Date: %s\r\n"
 		"%s"
 		"%s"
@@ -499,7 +515,7 @@ void ast_http_send(struct ast_tcptls_session_instance *ser,
 		"Content-Length: %d\r\n"
 		"\r\n",
 		status_code, status_title ? status_title : "OK",
-		ast_get_version(),
+		ast_str_buffer(server_header_field),
 		timebuf,
 		close_connection ? "Connection: close\r\n" : "",
 		static_content ? "" : "Cache-Control: no-cache, no-store\r\n",
@@ -532,6 +548,7 @@ void ast_http_send(struct ast_tcptls_session_instance *ser,
 
 	ast_free(http_header);
 	ast_free(out);
+	ast_free(server_header_field);
 
 	if (close_connection) {
 		ast_debug(1, "HTTP closing session.  status_code:%d\n", status_code);
@@ -541,76 +558,101 @@ void ast_http_send(struct ast_tcptls_session_instance *ser,
 	}
 }
 
-void ast_http_auth(struct ast_tcptls_session_instance *ser, const char *realm,
-	const unsigned long nonce, const unsigned long opaque, int stale,
-	const char *text)
+void ast_http_create_response(struct ast_tcptls_session_instance *ser, int status_code,
+	const char *status_title, struct ast_str *http_header_data, const char *text)
 {
-	struct ast_str *http_headers = ast_str_create(128);
-	struct ast_str *out = ast_str_create(512);
+	char server_name[MAX_SERVER_NAME_LENGTH];
+	struct ast_str *server_address = ast_str_create(MAX_SERVER_NAME_LENGTH);
+	struct ast_str *out = ast_str_create(MAX_CONTENT_LENGTH);
 
-	if (!http_headers || !out) {
-		ast_free(http_headers);
+	if (!http_header_data || !server_address || !out) {
+		ast_free(http_header_data);
+		ast_free(server_address);
 		ast_free(out);
 		if (ser && ser->f) {
-			ast_debug(1, "HTTP closing session.  Auth OOM\n");
+			ast_debug(1, "HTTP closing session. OOM.\n");
 			ast_tcptls_close_session_file(ser);
 		}
 		return;
 	}
 
-	ast_str_set(&http_headers, 0,
-		"WWW-authenticate: Digest algorithm=MD5, realm=\"%s\", nonce=\"%08lx\", qop=\"auth\", opaque=\"%08lx\"%s\r\n"
-		"Content-type: text/html\r\n",
-		realm ? realm : "Asterisk",
-		nonce,
-		opaque,
-		stale ? ", stale=true" : "");
-
-	ast_str_set(&out, 0,
-		"<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
-		"<html><head>\r\n"
-		"<title>401 Unauthorized</title>\r\n"
-		"</head><body>\r\n"
-		"<h1>401 Unauthorized</h1>\r\n"
-		"<p>%s</p>\r\n"
-		"<hr />\r\n"
-		"<address>Asterisk Server</address>\r\n"
-		"</body></html>\r\n",
-		text ? text : "");
-
-	ast_http_send(ser, AST_HTTP_UNKNOWN, 401, "Unauthorized", http_headers, out, 0, 0);
+	if(!ast_strlen_zero(http_server_name)) {
+		ast_xml_escape(http_server_name, server_name, sizeof(server_name));
+		ast_str_set(&server_address,
+	                0,
+	                "<address>%s</address>\r\n",
+	                server_name);
+	}
+
+	ast_str_set(&out,
+	            0,
+	            "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
+	            "<html><head>\r\n"
+	            "<title>%d %s</title>\r\n"
+	            "</head><body>\r\n"
+	            "<h1>%s</h1>\r\n"
+	            "<p>%s</p>\r\n"
+	            "<hr />\r\n"
+	            "%s"
+	            "</body></html>\r\n",
+	            status_code,
+	            status_title,
+	            status_title,
+	            text ? text : "",
+	            ast_str_buffer(server_address));
+
+	ast_free(server_address);
+
+	ast_http_send(ser,
+	              AST_HTTP_UNKNOWN,
+	              status_code,
+	              status_title,
+	              http_header_data,
+	              out,
+	              0,
+	              0);
 }
 
-void ast_http_error(struct ast_tcptls_session_instance *ser, int status_code, const char *status_title, const char *text)
+void ast_http_auth(struct ast_tcptls_session_instance *ser, const char *realm,
+	const unsigned long nonce, const unsigned long opaque, int stale,
+	const char *text)
 {
-	struct ast_str *http_headers = ast_str_create(40);
-	struct ast_str *out = ast_str_create(256);
-
-	if (!http_headers || !out) {
-		ast_free(http_headers);
-		ast_free(out);
-		if (ser && ser->f) {
-			ast_debug(1, "HTTP closing session.  error OOM\n");
-			ast_tcptls_close_session_file(ser);
-		}
-		return;
-	}
+	int status_code = 401;
+	char *status_title = "Unauthorized";
+	struct ast_str *http_header_data = ast_str_create(DEFAULT_RESPONSE_HEADER_LENGTH);
+
+	if (http_header_data) {
+		ast_str_set(&http_header_data,
+		            0,
+		            "WWW-authenticate: Digest algorithm=MD5, realm=\"%s\", nonce=\"%08lx\", qop=\"auth\", opaque=\"%08lx\"%s\r\n"
+		            "Content-type: text/html\r\n",
+		            realm ? realm : "Asterisk",
+		            nonce,
+		            opaque,
+		            stale ? ", stale=true" : "");
+	}
+
+	ast_http_create_response(ser,
+	                         status_code,
+	                         status_title,
+	                         http_header_data,
+	                         text);
+}
 
-	ast_str_set(&http_headers, 0, "Content-type: text/html\r\n");
+void ast_http_error(struct ast_tcptls_session_instance *ser, int status_code,
+	const char *status_title, const char *text)
+{
+	struct ast_str *http_header_data = ast_str_create(DEFAULT_RESPONSE_HEADER_LENGTH);
 
-	ast_str_set(&out, 0,
-		"<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
-		"<html><head>\r\n"
-		"<title>%d %s</title>\r\n"
-		"</head><body>\r\n"
-		"<h1>%s</h1>\r\n"
-		"<p>%s</p>\r\n"
-		"<hr />\r\n"
-		"<address>Asterisk Server</address>\r\n"
-		"</body></html>\r\n",
-		status_code, status_title, status_title, text);
+	if (http_header_data) {
+		ast_str_set(&http_header_data, 0, "Content-type: text/html\r\n");
+	}
 
-	ast_http_send(ser, AST_HTTP_UNKNOWN, status_code, status_title, http_headers, out, 0, 0);
+	ast_http_create_response(ser,
+	                         status_code,
+	                         status_title,
+	                         http_header_data,
+	                         text);
 }
 
 /*!
@@ -1030,8 +1072,8 @@ static int http_body_check_chunk_sync(struct ast_tcptls_session_instance *ser)
 		return -1;
 	}
 	if (chunk_sync[0] != 0x0D || chunk_sync[1] != 0x0A) {
-		ast_log(LOG_WARNING, "HTTP chunk sync bytes wrong (0x%02X, 0x%02X)\n",
-			(unsigned) chunk_sync[0], (unsigned) chunk_sync[1]);
+		ast_log(LOG_WARNING, "HTTP chunk sync bytes wrong (0x%02hhX, 0x%02hhX)\n",
+			(unsigned char) chunk_sync[0], (unsigned char) chunk_sync[1]);
 		return -1;
 	}
 
@@ -1831,6 +1873,11 @@ static int httpd_process_request(struct ast_tcptls_session_instance *ser)
 		return -1;
 	}
 
+	if (ast_shutdown_final()) {
+		ast_http_error(ser, 503, "Service Unavailable", "Shutdown in progress");
+		return -1;
+	}
+
 	/* process "Request Headers" lines */
 	if (http_request_headers_get(ser, &headers)) {
 		return -1;
@@ -2029,6 +2076,7 @@ static int __ast_http_load(int reload)
 	int enabled=0;
 	int newenablestatic=0;
 	char newprefix[MAX_PREFIX] = "";
+	char server_name[MAX_SERVER_NAME_LENGTH];
 	struct http_uri_redirect *redirect;
 	struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
 	uint32_t bindport = DEFAULT_PORT;
@@ -2054,10 +2102,13 @@ static int __ast_http_load(int reload)
 	}
 	http_tls_cfg.pvtfile = ast_strdup("");
 
+	/* Apply modern intermediate settings according to the Mozilla OpSec team as of July 30th, 2015 but disable TLSv1 */
+	ast_set_flag(&http_tls_cfg.flags, AST_SSL_DISABLE_TLSV1 | AST_SSL_SERVER_CIPHER_ORDER);
+
 	if (http_tls_cfg.cipher) {
 		ast_free(http_tls_cfg.cipher);
 	}
-	http_tls_cfg.cipher = ast_strdup("");
+	http_tls_cfg.cipher = ast_strdup("ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE- [...]
 
 	AST_RWLIST_WRLOCK(&uri_redirects);
 	while ((redirect = AST_RWLIST_REMOVE_HEAD(&uri_redirects, entry))) {
@@ -2071,6 +2122,8 @@ static int __ast_http_load(int reload)
 	session_inactivity = DEFAULT_SESSION_INACTIVITY;
 	session_keep_alive = DEFAULT_SESSION_KEEP_ALIVE;
 
+	snprintf(server_name, sizeof(server_name), "Asterisk/%s", ast_get_version());
+
 	v = ast_variable_browse(cfg, "general");
 	for (; v; v = v->next) {
 		/* read tls config options while preventing unsupported options from being set */
@@ -2081,13 +2134,17 @@ static int __ast_http_load(int reload)
 			&& strcasecmp(v->name, "tlsdontverifyserver")
 			&& strcasecmp(v->name, "tlsclientmethod")
 			&& strcasecmp(v->name, "sslclientmethod")
-			&& strcasecmp(v->name, "tlscipher")
-			&& strcasecmp(v->name, "sslcipher")
 			&& !ast_tls_read_conf(&http_tls_cfg, &https_desc, v->name, v->value)) {
 			continue;
 		}
 
-		if (!strcasecmp(v->name, "enabled")) {
+		if (!strcasecmp(v->name, "servername")) {
+			if (!ast_strlen_zero(v->value)) {
+				ast_copy_string(server_name, v->value, sizeof(server_name));
+			} else {
+				server_name[0] = '\0';
+			}
+		} else if (!strcasecmp(v->name, "enabled")) {
 			enabled = ast_true(v->value);
 		} else if (!strcasecmp(v->name, "enablestatic")) {
 			newenablestatic = ast_true(v->value);
@@ -2139,6 +2196,8 @@ static int __ast_http_load(int reload)
 	if (strcmp(prefix, newprefix)) {
 		ast_copy_string(prefix, newprefix, sizeof(prefix));
 	}
+
+	ast_copy_string(http_server_name, server_name, sizeof(http_server_name));
 	enablestatic = newenablestatic;
 
 	if (num_addrs && enabled) {
@@ -2209,6 +2268,7 @@ static char *handle_show_http(struct ast_cli_entry *e, int cmd, struct ast_cli_a
 	}
 	ast_cli(a->fd, "HTTP Server Status:\n");
 	ast_cli(a->fd, "Prefix: %s\n", prefix);
+	ast_cli(a->fd, "Server: %s\n", http_server_name);
 	if (ast_sockaddr_isnull(&http_desc.old_address)) {
 		ast_cli(a->fd, "Server Disabled\n\n");
 	} else {
@@ -2279,7 +2339,7 @@ int ast_http_init(void)
 	ast_http_uri_link(&statusuri);
 	ast_http_uri_link(&staticuri);
 	ast_cli_register_multiple(cli_http, ARRAY_LEN(cli_http));
-	ast_register_atexit(http_shutdown);
+	ast_register_cleanup(http_shutdown);
 
 	return __ast_http_load(0);
 }
diff --git a/main/image.c b/main/image.c
index 4493452..118cd17 100644
--- a/main/image.c
+++ b/main/image.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419044 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/time.h>
 #include <sys/stat.h>
@@ -214,6 +214,6 @@ static void image_shutdown(void)
 int ast_image_init(void)
 {
 	ast_cli_register_multiple(cli_image, ARRAY_LEN(cli_image));
-	ast_register_atexit(image_shutdown);
+	ast_register_cleanup(image_shutdown);
 	return 0;
 }
diff --git a/main/indications.c b/main/indications.c
index 5977826..02a68b7 100644
--- a/main/indications.c
+++ b/main/indications.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419044 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <math.h>
 
@@ -359,14 +359,13 @@ int ast_playtones_start(struct ast_channel *chan, int vol, const char *playlst,
 
 		if (tone_data.midinote) {
 			/* midi notes must be between 0 and 127 */
-
-			if (tone_data.freq1 >= 0 && tone_data.freq1 <= 127) {
+			if (tone_data.freq1 <= 127) {
 				tone_data.freq1 = midi_tohz[tone_data.freq1];
 			} else {
 				tone_data.freq1 = 0;
 			}
 
-			if (tone_data.freq2 >= 0 && tone_data.freq2 <= 127) {
+			if (tone_data.freq2 <= 127) {
 				tone_data.freq2 = midi_tohz[tone_data.freq2];
 			} else {
 				tone_data.freq2 = 0;
@@ -1186,7 +1185,7 @@ int ast_indications_init(void)
 
 	ast_cli_register_multiple(cli_indications, ARRAY_LEN(cli_indications));
 
-	ast_register_atexit(indications_shutdown);
+	ast_register_cleanup(indications_shutdown);
 	return 0;
 }
 
diff --git a/main/io.c b/main/io.c
index 540c0b6..cd35995 100644
--- a/main/io.c
+++ b/main/io.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 413589 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <termios.h>
 #include <sys/ioctl.h>
diff --git a/main/jitterbuf.c b/main/jitterbuf.c
index e8bf836..1bfe508 100644
--- a/main/jitterbuf.c
+++ b/main/jitterbuf.c
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 401789 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "jitterbuf.h"
 #include "asterisk/utils.h"
@@ -139,7 +139,7 @@ static int check_resync(jitterbuf *jb, long ts, long now, long ms, const enum jb
 
 	/* check for drastic change in delay */
 	if (jb->info.conf.resync_threshold != -1) {
-		if (abs(*delay - jb->info.last_delay) > threshold) {
+		if (labs(*delay - jb->info.last_delay) > threshold) {
 			jb->info.cnt_delay_discont++;
 			/* resync the jitterbuffer on 3 consecutive discontinuities,
 			 * or immediately if a control frame */
diff --git a/main/json.c b/main/json.c
index 24bd096..35e6f16 100644
--- a/main/json.c
+++ b/main/json.c
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420098 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/json.h"
 #include "asterisk/localtime.h"
@@ -882,32 +882,47 @@ struct ast_json *ast_json_party_id(struct ast_party_id *party)
 	return ast_json_ref(json_party_id);
 }
 
-int ast_json_to_ast_variables(struct ast_json *json_variables, struct ast_variable **variables)
+enum ast_json_to_ast_vars_code ast_json_to_ast_variables(struct ast_json *json_variables, struct ast_variable **variables)
 {
 	struct ast_json_iter *it_json_var;
 
 	*variables = NULL;
 
 	for (it_json_var = ast_json_object_iter(json_variables); it_json_var;
-		 it_json_var = ast_json_object_iter_next(json_variables, it_json_var)) {
+		it_json_var = ast_json_object_iter_next(json_variables, it_json_var)) {
 		struct ast_variable *new_var;
 		const char *key = ast_json_object_iter_key(it_json_var);
+		const char *value;
+		struct ast_json *json_value;
 
 		if (ast_strlen_zero(key)) {
 			continue;
 		}
 
-		new_var = ast_variable_new(key,
-		                           ast_json_string_get(ast_json_object_iter_value(it_json_var)),
-		                           "");
+		json_value = ast_json_object_iter_value(it_json_var);
+		if (ast_json_typeof(json_value) != AST_JSON_STRING) {
+			/* Error: Only strings allowed */
+			ast_variables_destroy(*variables);
+			*variables = NULL;
+			return AST_JSON_TO_AST_VARS_CODE_INVALID_TYPE;
+		}
+		value = ast_json_string_get(json_value);
+		/* Should never be NULL.  Otherwise, how could it be a string type? */
+		ast_assert(value != NULL);
+		if (!value) {
+			/* To be safe. */
+			continue;
+		}
+		new_var = ast_variable_new(key, value, "");
 		if (!new_var) {
+			/* Error: OOM */
 			ast_variables_destroy(*variables);
 			*variables = NULL;
-			return -1;
+			return AST_JSON_TO_AST_VARS_CODE_OOM;
 		}
 
 		ast_variable_list_append(variables, new_var);
 	}
 
-	return 0;
+	return AST_JSON_TO_AST_VARS_CODE_SUCCESS;
 }
diff --git a/main/libasteriskssl.c b/main/libasteriskssl.c
index bd5913e..b326701 100644
--- a/main/libasteriskssl.c
+++ b/main/libasteriskssl.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 373080 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #ifdef HAVE_OPENSSL
 #include <openssl/ssl.h>
@@ -93,33 +93,6 @@ void SSL_load_error_strings(void)
 #endif
 }
 
-void ERR_load_SSL_strings(void)
-{
-#if defined(AST_DEVMODE)
-	if (startup_complete) {
-		ast_debug(1, "Called after startup... ignoring!\n");
-	}
-#endif
-}
-
-void ERR_load_crypto_strings(void)
-{
-#if defined(AST_DEVMODE)
-	if (startup_complete) {
-		ast_debug(1, "Called after startup... ignoring!\n");
-	}
-#endif
-}
-
-void ERR_load_BIO_strings(void)
-{
-#if defined(AST_DEVMODE)
-	if (startup_complete) {
-		ast_debug(1, "Called after startup... ignoring!\n");
-	}
-#endif
-}
-
 void CRYPTO_set_id_callback(unsigned long (*func)(void))
 {
 #if defined(AST_DEVMODE)
@@ -157,8 +130,6 @@ int ast_ssl_init(void)
 	void (*real_CRYPTO_set_id_callback)(unsigned long (*)(void));
 	void (*real_CRYPTO_set_locking_callback)(void (*)(int, int, const char *, int));
 	void (*real_SSL_load_error_strings)(void);
-	void (*real_ERR_load_SSL_strings)(void);
-	void (*real_ERR_load_BIO_strings)(void);
 	const char *errstr;
 
 	/* clear any previous dynamic linker errors */
@@ -216,12 +187,6 @@ int ast_ssl_init(void)
 	get_OpenSSL_function(SSL_load_error_strings);
 	real_SSL_load_error_strings();
 
-	get_OpenSSL_function(ERR_load_SSL_strings);
-	real_ERR_load_SSL_strings();
-
-	get_OpenSSL_function(ERR_load_BIO_strings);
-	real_ERR_load_BIO_strings();
-
 	startup_complete = 1;
 
 #endif /* HAVE_OPENSSL */
diff --git a/main/loader.c b/main/loader.c
index ed5bd9e..2b3bd1f 100644
--- a/main/loader.c
+++ b/main/loader.c
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420124 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/_private.h"
 #include "asterisk/paths.h"	/* use ast_config_AST_MODULE_DIR */
@@ -115,14 +115,24 @@ static unsigned int embedding = 1; /* we always start out by registering embedde
 				      since they are here before we dlopen() any
 				   */
 
+/*!
+ * \brief Internal flag to indicate all modules have been initially loaded.
+ */
+static int modules_loaded;
+
 struct ast_module {
 	const struct ast_module_info *info;
+#ifdef REF_DEBUG
+	/* Used to get module references into REF_DEBUG logs */
+	void *ref_debug;
+#endif
 	void *lib;					/* the shared lib, or NULL if embedded */
 	int usecount;					/* the number of 'users' currently in this module */
 	struct module_user_list users;			/* the list of users in the module */
 	struct {
 		unsigned int running:1;
 		unsigned int declined:1;
+		unsigned int keepuntilshutdown:1;
 	} flags;
 	AST_LIST_ENTRY(ast_module) list_entry;
 	AST_DLLIST_ENTRY(ast_module) entry;
@@ -189,6 +199,9 @@ void ast_module_register(const struct ast_module_info *info)
 	ast_debug(5, "Registering module %s\n", info->name);
 
 	mod->info = info;
+#ifdef REF_DEBUG
+	mod->ref_debug = ao2_t_alloc(0, NULL, info->name);
+#endif
 	AST_LIST_HEAD_INIT(&mod->users);
 
 	/* during startup, before the loader has been initialized,
@@ -235,6 +248,9 @@ void ast_module_unregister(const struct ast_module_info *info)
 	if (mod) {
 		ast_debug(5, "Unregistering module %s\n", info->name);
 		AST_LIST_HEAD_DESTROY(&mod->users);
+#ifdef REF_DEBUG
+		ao2_cleanup(mod->ref_debug);
+#endif
 		ast_free(mod);
 	}
 }
@@ -254,6 +270,10 @@ struct ast_module_user *__ast_module_user_add(struct ast_module *mod, struct ast
 	AST_LIST_INSERT_HEAD(&mod->users, u, entry);
 	AST_LIST_UNLOCK(&mod->users);
 
+#ifdef REF_DEBUG
+	ao2_ref(mod->ref_debug, +1);
+#endif
+
 	ast_atomic_fetchadd_int(&mod->usecount, +1);
 
 	ast_update_use_count();
@@ -278,6 +298,10 @@ void __ast_module_user_remove(struct ast_module *mod, struct ast_module_user *u)
 		return;
 	}
 
+#ifdef REF_DEBUG
+	ao2_ref(mod->ref_debug, -1);
+#endif
+
 	ast_atomic_fetchadd_int(&mod->usecount, -1);
 	ast_free(u);
 
@@ -293,6 +317,11 @@ void __ast_module_user_hangup_all(struct ast_module *mod)
 		if (u->chan) {
 			ast_softhangup(u->chan, AST_SOFTHANGUP_APPUNLOAD);
 		}
+
+#ifdef REF_DEBUG
+		ao2_ref(mod->ref_debug, -1);
+#endif
+
 		ast_atomic_fetchadd_int(&mod->usecount, -1);
 		ast_free(u);
 	}
@@ -334,7 +363,7 @@ static int printdigest(const unsigned char *d)
 	char buf[256]; /* large enough so we don't have to worry */
 
 	for (pos = 0, x = 0; x < 16; x++)
-		pos += sprintf(buf + pos, " %02x", (unsigned)*d++);
+		pos += sprintf(buf + pos, " %02hhx", *d++);
 
 	ast_debug(1, "Unexpected signature:%s\n", buf);
 
@@ -459,7 +488,9 @@ static int is_module_loaded(const char *resource_name)
 
 static void unload_dynamic_module(struct ast_module *mod)
 {
+#if defined(HAVE_RTLD_NOLOAD)
 	char *name = ast_strdupa(ast_module_name(mod));
+#endif
 	void *lib = mod->lib;
 
 	/* WARNING: the structure pointed to by mod is going to
@@ -610,10 +641,22 @@ void ast_module_shutdown(void)
 				mod->info->unload();
 			}
 			AST_LIST_HEAD_DESTROY(&mod->users);
+#ifdef REF_DEBUG
+			ao2_cleanup(mod->ref_debug);
+#endif
 			free(mod);
 			somethingchanged = 1;
 		}
 		AST_DLLIST_TRAVERSE_BACKWARDS_SAFE_END;
+		if (!somethingchanged) {
+			AST_DLLIST_TRAVERSE(&module_list, mod, entry) {
+				if (mod->flags.keepuntilshutdown) {
+					ast_module_unref(mod);
+					mod->flags.keepuntilshutdown = 0;
+					somethingchanged = 1;
+				}
+			}
+		}
 	} while (somethingchanged && !final);
 
 	AST_DLLIST_UNLOCK(&module_list);
@@ -729,9 +772,7 @@ void ast_process_pending_reloads(void)
 {
 	struct reload_queue_item *item;
 
-	if (!ast_fully_booted) {
-		return;
-	}
+	modules_loaded = 1;
 
 	AST_LIST_LOCK(&reload_queue);
 
@@ -811,10 +852,10 @@ static void publish_reload_message(const char *name, enum ast_module_reload_resu
 	event_object = ast_json_pack("{s: s, s: s}",
 			"Module", S_OR(name, "All"),
 			"Status", res_buffer);
-	json_object = ast_json_pack("{s: s, s: i, s: O}",
+	json_object = ast_json_pack("{s: s, s: i, s: o}",
 			"type", "Reload",
 			"class_type", EVENT_FLAG_SYSTEM,
-			"event", event_object);
+			"event", ast_json_ref(event_object));
 
 	if (!json_object) {
 		return;
@@ -841,7 +882,7 @@ enum ast_module_reload_result ast_module_reload(const char *name)
 
 	/* If we aren't fully booted, we just pretend we reloaded but we queue this
 	   up to run once we are booted up. */
-	if (!ast_fully_booted) {
+	if (!modules_loaded) {
 		queue_reload_request(name);
 		res = AST_MODULE_RELOAD_QUEUED;
 		goto module_reload_exit;
@@ -1384,6 +1425,60 @@ int ast_update_module_list(int (*modentry)(const char *module, const char *descr
 	return total_mod_loaded;
 }
 
+int ast_update_module_list_data(int (*modentry)(const char *module, const char *description,
+                                                int usecnt, const char *status, const char *like,
+                                                enum ast_module_support_level support_level,
+                                                void *data),
+                                const char *like, void *data)
+{
+	struct ast_module *cur;
+	int total_mod_loaded = 0;
+	AST_LIST_HEAD_NOLOCK(, ast_module) alpha_module_list = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
+
+	AST_DLLIST_LOCK(&module_list);
+
+	AST_DLLIST_TRAVERSE(&module_list, cur, entry) {
+		AST_LIST_INSERT_SORTALPHA(&alpha_module_list, cur, list_entry, resource);
+	}
+
+	while ((cur = AST_LIST_REMOVE_HEAD(&alpha_module_list, list_entry))) {
+		total_mod_loaded += modentry(cur->resource, cur->info->description, cur->usecount,
+		        cur->flags.running? "Running" : "Not Running", like, cur->info->support_level, data);
+	}
+
+	AST_DLLIST_UNLOCK(&module_list);
+
+	return total_mod_loaded;
+}
+
+int ast_update_module_list_condition(int (*modentry)(const char *module, const char *description,
+                                                     int usecnt, const char *status,
+                                                     const char *like,
+                                                     enum ast_module_support_level support_level,
+                                                     void *data, const char *condition),
+                                     const char *like, void *data, const char *condition)
+{
+	struct ast_module *cur;
+	int conditions_met = 0;
+	AST_LIST_HEAD_NOLOCK(, ast_module) alpha_module_list = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
+
+	AST_DLLIST_LOCK(&module_list);
+
+	AST_DLLIST_TRAVERSE(&module_list, cur, entry) {
+		AST_LIST_INSERT_SORTALPHA(&alpha_module_list, cur, list_entry, resource);
+	}
+
+	while ((cur = AST_LIST_REMOVE_HEAD(&alpha_module_list, list_entry))) {
+		conditions_met += modentry(cur->resource, cur->info->description, cur->usecount,
+		        cur->flags.running? "Running" : "Not Running", like, cur->info->support_level, data,
+		        condition);
+	}
+
+	AST_DLLIST_UNLOCK(&module_list);
+
+	return conditions_met;
+}
+
 /*! \brief Check if module exists */
 int ast_module_check(const char *name)
 {
@@ -1430,24 +1525,42 @@ int ast_loader_unregister(int (*v)(void))
 	return cur ? 0 : -1;
 }
 
-struct ast_module *ast_module_ref(struct ast_module *mod)
+struct ast_module *__ast_module_ref(struct ast_module *mod, const char *file, int line, const char *func)
 {
 	if (!mod) {
 		return NULL;
 	}
 
+#ifdef REF_DEBUG
+	__ao2_ref_debug(mod->ref_debug, +1, "", file, line, func);
+#endif
+
 	ast_atomic_fetchadd_int(&mod->usecount, +1);
 	ast_update_use_count();
 
 	return mod;
 }
 
-void ast_module_unref(struct ast_module *mod)
+void __ast_module_shutdown_ref(struct ast_module *mod, const char *file, int line, const char *func)
+{
+	if (!mod || mod->flags.keepuntilshutdown) {
+		return;
+	}
+
+	__ast_module_ref(mod, file, line, func);
+	mod->flags.keepuntilshutdown = 1;
+}
+
+void __ast_module_unref(struct ast_module *mod, const char *file, int line, const char *func)
 {
 	if (!mod) {
 		return;
 	}
 
+#ifdef REF_DEBUG
+	__ao2_ref_debug(mod->ref_debug, -1, "", file, line, func);
+#endif
+
 	ast_atomic_fetchadd_int(&mod->usecount, -1);
 	ast_update_use_count();
 }
@@ -1463,3 +1576,22 @@ const char *ast_module_support_level_to_string(enum ast_module_support_level sup
 {
 	return support_level_map[support_level];
 }
+
+
+
+/* The following exists for ABI compatibility */
+#undef ast_module_ref
+#undef ast_module_unref
+
+struct ast_module *ast_module_ref(struct ast_module *mod);
+void ast_module_unref(struct ast_module *mod);
+
+struct ast_module *ast_module_ref(struct ast_module *mod)
+{
+	return __ast_module_ref(mod, __FILE__, __LINE__, __PRETTY_FUNCTION__);
+}
+
+void ast_module_unref(struct ast_module *mod)
+{
+	__ast_module_unref(mod, __FILE__, __LINE__, __PRETTY_FUNCTION__);
+}
diff --git a/main/lock.c b/main/lock.c
index d178194..dd90d7b 100644
--- a/main/lock.c
+++ b/main/lock.c
@@ -27,7 +27,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 411087 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/utils.h"
 #include "asterisk/lock.h"
@@ -62,6 +62,66 @@ static void __dump_backtrace(struct ast_bt *bt, int canlog)
 }
 #endif	/* defined(DEBUG_THREADS) && defined(HAVE_BKTR) */
 
+#ifdef DEBUG_THREADS
+AST_MUTEX_DEFINE_STATIC(reentrancy_lock);
+
+static inline struct ast_lock_track *ast_get_reentrancy(struct ast_lock_track **plt)
+{
+	pthread_mutexattr_t reentr_attr;
+	struct ast_lock_track *lt;
+
+	/* It's a bit painful to lock a global mutex for every access to the
+	 * reentrancy structure, but it's necessary to ensure that we don't
+	 * double-allocate the structure or double-initialize the reentr_mutex.
+	 *
+	 * If you'd like to replace this with a double-checked lock, be sure to
+	 * properly volatile-ize everything to avoid optimizer bugs.
+	 *
+	 * We also have to use the underlying pthread calls for manipulating
+	 * the mutex, because this is called from the Asterisk mutex code.
+	 */
+	pthread_mutex_lock(&reentrancy_lock.mutex);
+
+	if (*plt) {
+		pthread_mutex_unlock(&reentrancy_lock.mutex);
+		return *plt;
+	}
+
+	lt = *plt = ast_std_calloc(1, sizeof(*lt));
+	if (!lt) {
+		fprintf(stderr, "%s: Failed to allocate lock tracking\n", __func__);
+#if defined(DO_CRASH) || defined(THREAD_CRASH)
+		abort();
+#else
+		pthread_mutex_unlock(&reentrancy_lock.mutex);
+		return NULL;
+#endif
+	}
+
+	pthread_mutexattr_init(&reentr_attr);
+	pthread_mutexattr_settype(&reentr_attr, AST_MUTEX_KIND);
+	pthread_mutex_init(&lt->reentr_mutex, &reentr_attr);
+	pthread_mutexattr_destroy(&reentr_attr);
+
+	pthread_mutex_unlock(&reentrancy_lock.mutex);
+	return lt;
+}
+
+static inline void delete_reentrancy_cs(struct ast_lock_track **plt)
+{
+	struct ast_lock_track *lt;
+
+	if (*plt) {
+		lt = *plt;
+		*plt = NULL;
+
+		pthread_mutex_destroy(&lt->reentr_mutex);
+		ast_std_free(lt);
+	}
+}
+
+#endif /* DEBUG_THREADS */
+
 int __ast_pthread_mutex_init(int tracking, const char *filename, int lineno, const char *func,
 						const char *mutex_name, ast_mutex_t *t)
 {
@@ -69,30 +129,26 @@ int __ast_pthread_mutex_init(int tracking, const char *filename, int lineno, con
 	pthread_mutexattr_t  attr;
 
 #ifdef DEBUG_THREADS
-	t->track = NULL;
 #if defined(AST_MUTEX_INIT_W_CONSTRUCTORS) && defined(CAN_COMPARE_MUTEX_TO_INIT_VALUE)
 	if ((t->mutex) != ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
-/*
-		int canlog = strcmp(filename, "logger.c") & track;
+		int canlog = tracking && strcmp(filename, "logger.c");
+
 		__ast_mutex_logger("%s line %d (%s): NOTICE: mutex '%s' is already initialized.\n",
 				   filename, lineno, func, mutex_name);
 		DO_THREAD_CRASH;
-*/
-		return 0;
+		return EBUSY;
 	}
-
 #endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
 
-	if ((t->tracking = tracking)) {
-		ast_reentrancy_init(&t->track);
-	}
+	t->track = NULL;
+	t->tracking = tracking;
 #endif /* DEBUG_THREADS */
 
 	pthread_mutexattr_init(&attr);
 	pthread_mutexattr_settype(&attr, AST_MUTEX_KIND);
-
 	res = pthread_mutex_init(&t->mutex, &attr);
 	pthread_mutexattr_destroy(&attr);
+
 	return res;
 }
 
@@ -102,28 +158,25 @@ int __ast_pthread_mutex_destroy(const char *filename, int lineno, const char *fu
 	int res;
 
 #ifdef DEBUG_THREADS
-	struct ast_lock_track *lt;
-	int canlog = strcmp(filename, "logger.c") & t->tracking;
+	struct ast_lock_track *lt = t->track;
+	int canlog = t->tracking && strcmp(filename, "logger.c");
 
 #if defined(AST_MUTEX_INIT_W_CONSTRUCTORS) && defined(CAN_COMPARE_MUTEX_TO_INIT_VALUE)
 	if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
-		/* Don't try to uninitialize non initialized mutex
-		 * This may no effect on linux
-		 * And always ganerate core on *BSD with
-		 * linked libpthread
-		 * This not error condition if the mutex created on the fly.
+		/* Don't try to uninitialize an uninitialized mutex
+		 * This may have no effect on linux
+		 * but it always generates a core on *BSD when
+		 * linked with libpthread.
+		 * This is not an error condition if the mutex is created on the fly.
 		 */
 		__ast_mutex_logger("%s line %d (%s): NOTICE: mutex '%s' is uninitialized.\n",
 				   filename, lineno, func, mutex_name);
-		return 0;
+		DO_THREAD_CRASH;
+		res = EINVAL;
+		goto lt_cleanup;
 	}
 #endif
 
-	if (t->tracking && !t->track) {
-		ast_reentrancy_init(&t->track);
-	}
-	lt = t->track;
-
 	res = pthread_mutex_trylock(&t->mutex);
 	switch (res) {
 	case 0:
@@ -136,7 +189,7 @@ int __ast_pthread_mutex_destroy(const char *filename, int lineno, const char *fu
 	case EBUSY:
 		__ast_mutex_logger("%s line %d (%s): Error: attempt to destroy locked mutex '%s'.\n",
 				   filename, lineno, func, mutex_name);
-		if (t->tracking) {
+		if (lt) {
 			ast_reentrancy_lock(lt);
 			__ast_mutex_logger("%s line %d (%s): Error: '%s' was locked here.\n",
 				    lt->file[ROFFSET], lt->lineno[ROFFSET], lt->func[ROFFSET], mutex_name);
@@ -156,7 +209,10 @@ int __ast_pthread_mutex_destroy(const char *filename, int lineno, const char *fu
 		__ast_mutex_logger("%s line %d (%s): Error destroying mutex %s: %s\n",
 				   filename, lineno, func, mutex_name, strerror(res));
 	}
-	if (t->tracking) {
+#if defined(AST_MUTEX_INIT_W_CONSTRUCTORS) && defined(CAN_COMPARE_MUTEX_TO_INIT_VALUE)
+lt_cleanup:
+#endif
+	if (lt) {
 		ast_reentrancy_lock(lt);
 		lt->file[0] = filename;
 		lt->lineno[0] = lineno;
@@ -180,33 +236,17 @@ int __ast_pthread_mutex_lock(const char *filename, int lineno, const char *func,
 	int res;
 
 #ifdef DEBUG_THREADS
-	struct ast_lock_track *lt;
-	int canlog = strcmp(filename, "logger.c") & t->tracking;
+	struct ast_lock_track *lt = NULL;
+	int canlog = t->tracking && strcmp(filename, "logger.c");
 #ifdef HAVE_BKTR
 	struct ast_bt *bt = NULL;
 #endif
 
-#if defined(AST_MUTEX_INIT_W_CONSTRUCTORS) && defined(CAN_COMPARE_MUTEX_TO_INIT_VALUE)
-	if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
-		/* Don't warn abount uninitialized mutex.
-		 * Simple try to initialize it.
-		 * May be not needed in linux system.
-		 */
-		res = __ast_pthread_mutex_init(t->tracking, filename, lineno, func, mutex_name, t);
-		if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
-			__ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized and unable to initialize.\n",
-					 filename, lineno, func, mutex_name);
-			return res;
-		}
-	}
-#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
-
-	if (t->tracking && !t->track) {
-		ast_reentrancy_init(&t->track);
+	if (t->tracking) {
+		lt = ast_get_reentrancy(&t->track);
 	}
-	lt = t->track;
 
-	if (t->tracking) {
+	if (lt) {
 #ifdef HAVE_BKTR
 		struct ast_bt tmp;
 
@@ -216,7 +256,7 @@ int __ast_pthread_mutex_lock(const char *filename, int lineno, const char *func,
 		ast_bt_get_addresses(&tmp);
 
 		ast_reentrancy_lock(lt);
-		if (lt->reentrancy != AST_MAX_REENTRANCY) {
+		if (lt->reentrancy < AST_MAX_REENTRANCY) {
 			lt->backtrace[lt->reentrancy] = tmp;
 			bt = &lt->backtrace[lt->reentrancy];
 		}
@@ -274,7 +314,7 @@ int __ast_pthread_mutex_lock(const char *filename, int lineno, const char *func,
 #endif /* !DETECT_DEADLOCKS || !DEBUG_THREADS */
 
 #ifdef DEBUG_THREADS
-	if (t->tracking && !res) {
+	if (lt && !res) {
 		ast_reentrancy_lock(lt);
 		if (lt->reentrancy < AST_MAX_REENTRANCY) {
 			lt->file[lt->reentrancy] = filename;
@@ -287,10 +327,8 @@ int __ast_pthread_mutex_lock(const char *filename, int lineno, const char *func,
 							   filename, lineno, func, mutex_name);
 		}
 		ast_reentrancy_unlock(lt);
-		if (t->tracking) {
-			ast_mark_lock_acquired(t);
-		}
-	} else if (t->tracking) {
+		ast_mark_lock_acquired(t);
+	} else if (lt) {
 #ifdef HAVE_BKTR
 		if (lt->reentrancy) {
 			ast_reentrancy_lock(lt);
@@ -320,33 +358,17 @@ int __ast_pthread_mutex_trylock(const char *filename, int lineno, const char *fu
 	int res;
 
 #ifdef DEBUG_THREADS
-	struct ast_lock_track *lt;
-	int canlog = strcmp(filename, "logger.c") & t->tracking;
+	struct ast_lock_track *lt = NULL;
+	int canlog = t->tracking && strcmp(filename, "logger.c");
 #ifdef HAVE_BKTR
 	struct ast_bt *bt = NULL;
 #endif
 
-#if defined(AST_MUTEX_INIT_W_CONSTRUCTORS) && defined(CAN_COMPARE_MUTEX_TO_INIT_VALUE)
-	if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
-		/* Don't warn abount uninitialized mutex.
-		 * Simple try to initialize it.
-		 * May be not needed in linux system.
-		 */
-		res = __ast_pthread_mutex_init(t->tracking, filename, lineno, func, mutex_name, t);
-		if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
-			__ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized and unable to initialize.\n",
-					 filename, lineno, func, mutex_name);
-			return res;
-		}
-	}
-#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
-
-	if (t->tracking && !t->track) {
-		ast_reentrancy_init(&t->track);
+	if (t->tracking) {
+		lt = ast_get_reentrancy(&t->track);
 	}
-	lt = t->track;
 
-	if (t->tracking) {
+	if (lt) {
 #ifdef HAVE_BKTR
 		struct ast_bt tmp;
 
@@ -356,7 +378,7 @@ int __ast_pthread_mutex_trylock(const char *filename, int lineno, const char *fu
 		ast_bt_get_addresses(&tmp);
 
 		ast_reentrancy_lock(lt);
-		if (lt->reentrancy != AST_MAX_REENTRANCY) {
+		if (lt->reentrancy < AST_MAX_REENTRANCY) {
 			lt->backtrace[lt->reentrancy] = tmp;
 			bt = &lt->backtrace[lt->reentrancy];
 		}
@@ -372,7 +394,7 @@ int __ast_pthread_mutex_trylock(const char *filename, int lineno, const char *fu
 	res = pthread_mutex_trylock(&t->mutex);
 
 #ifdef DEBUG_THREADS
-	if (t->tracking && !res) {
+	if (lt && !res) {
 		ast_reentrancy_lock(lt);
 		if (lt->reentrancy < AST_MAX_REENTRANCY) {
 			lt->file[lt->reentrancy] = filename;
@@ -385,10 +407,8 @@ int __ast_pthread_mutex_trylock(const char *filename, int lineno, const char *fu
 					   filename, lineno, func, mutex_name);
 		}
 		ast_reentrancy_unlock(lt);
-		if (t->tracking) {
-			ast_mark_lock_acquired(t);
-		}
-	} else if (t->tracking) {
+		ast_mark_lock_acquired(t);
+	} else if (lt) {
 		ast_mark_lock_failed(t);
 	}
 #endif /* DEBUG_THREADS */
@@ -402,8 +422,8 @@ int __ast_pthread_mutex_unlock(const char *filename, int lineno, const char *fun
 	int res;
 
 #ifdef DEBUG_THREADS
-	struct ast_lock_track *lt;
-	int canlog = strcmp(filename, "logger.c") & t->tracking;
+	struct ast_lock_track *lt = NULL;
+	int canlog = t->tracking && strcmp(filename, "logger.c");
 #ifdef HAVE_BKTR
 	struct ast_bt *bt = NULL;
 #endif
@@ -412,21 +432,16 @@ int __ast_pthread_mutex_unlock(const char *filename, int lineno, const char *fun
 	if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
 		__ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized.\n",
 				   filename, lineno, func, mutex_name);
-		res = __ast_pthread_mutex_init(t->tracking, filename, lineno, func, mutex_name, t);
-		if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
-			__ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized and unable to initialize.\n",
-					 filename, lineno, func, mutex_name);
-		}
-		return res;
+		DO_THREAD_CRASH;
+		return EINVAL;
 	}
 #endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
 
-	if (t->tracking && !t->track) {
-		ast_reentrancy_init(&t->track);
+	if (t->tracking) {
+		lt = ast_get_reentrancy(&t->track);
 	}
-	lt = t->track;
 
-	if (t->tracking) {
+	if (lt) {
 		ast_reentrancy_lock(lt);
 		if (lt->reentrancy && (lt->thread[ROFFSET] != pthread_self())) {
 			__ast_mutex_logger("%s line %d (%s): attempted unlock mutex '%s' without owning it!\n",
@@ -505,6 +520,29 @@ int __ast_cond_destroy(const char *filename, int lineno, const char *func,
 	return pthread_cond_destroy(cond);
 }
 
+#ifdef DEBUG_THREADS
+static void restore_lock_tracking(struct ast_lock_track *lt, struct ast_lock_track *lt_saved)
+{
+	ast_reentrancy_lock(lt);
+
+	/*
+	 * The following code must match the struct ast_lock_track
+	 * definition with the explicit exception of the reentr_mutex
+	 * member.
+	 */
+	memcpy(lt->file, lt_saved->file, sizeof(lt->file));
+	memcpy(lt->lineno, lt_saved->lineno, sizeof(lt->lineno));
+	lt->reentrancy = lt_saved->reentrancy;
+	memcpy(lt->func, lt_saved->func, sizeof(lt->func));
+	memcpy(lt->thread, lt_saved->thread, sizeof(lt->thread));
+#ifdef HAVE_BKTR
+	memcpy(lt->backtrace, lt_saved->backtrace, sizeof(lt->backtrace));
+#endif
+
+	ast_reentrancy_unlock(lt);
+}
+#endif /* DEBUG_THREADS */
+
 int __ast_cond_wait(const char *filename, int lineno, const char *func,
 				  const char *cond_name, const char *mutex_name,
 				  ast_cond_t *cond, ast_mutex_t *t)
@@ -512,32 +550,27 @@ int __ast_cond_wait(const char *filename, int lineno, const char *func,
 	int res;
 
 #ifdef DEBUG_THREADS
-	struct ast_lock_track *lt;
+	struct ast_lock_track *lt = NULL;
 	struct ast_lock_track lt_orig;
-	int canlog = strcmp(filename, "logger.c") & t->tracking;
+	int canlog = t->tracking && strcmp(filename, "logger.c");
 
 #if defined(AST_MUTEX_INIT_W_CONSTRUCTORS) && defined(CAN_COMPARE_MUTEX_TO_INIT_VALUE)
 	if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
 		__ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized.\n",
 				   filename, lineno, func, mutex_name);
-		res = __ast_pthread_mutex_init(t->tracking, filename, lineno, func, mutex_name, t);
-		if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
-			__ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized and unable to initialize.\n",
-					 filename, lineno, func, mutex_name);
-		}
-		return res;
+		DO_THREAD_CRASH;
+		return EINVAL;
 	}
 #endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
 
-	if (t->tracking && !t->track) {
-		ast_reentrancy_init(&t->track);
+	if (t->tracking) {
+		lt = ast_get_reentrancy(&t->track);
 	}
-	lt = t->track;
 
-	if (t->tracking) {
+	if (lt) {
 		ast_reentrancy_lock(lt);
 		if (lt->reentrancy && (lt->thread[ROFFSET] != pthread_self())) {
-			__ast_mutex_logger("%s line %d (%s): attempted unlock mutex '%s' without owning it!\n",
+			__ast_mutex_logger("%s line %d (%s): attempted wait using mutex '%s' without owning it!\n",
 					   filename, lineno, func, mutex_name);
 			__ast_mutex_logger("%s line %d (%s): '%s' was locked here.\n",
 					   lt->file[ROFFSET], lt->lineno[ROFFSET], lt->func[ROFFSET], mutex_name);
@@ -546,7 +579,7 @@ int __ast_cond_wait(const char *filename, int lineno, const char *func,
 #endif
 			DO_THREAD_CRASH;
 		} else if (lt->reentrancy <= 0) {
-			__ast_mutex_logger("%s line %d (%s): attempted to wait on an unlocked mutex '%s'\n",
+			__ast_mutex_logger("%s line %d (%s): attempted wait using an unlocked mutex '%s'\n",
 					   filename, lineno, func, mutex_name);
 			DO_THREAD_CRASH;
 		}
@@ -569,16 +602,8 @@ int __ast_cond_wait(const char *filename, int lineno, const char *func,
 		__ast_mutex_logger("%s line %d (%s): Error waiting on condition mutex '%s'\n",
 				   filename, lineno, func, strerror(res));
 		DO_THREAD_CRASH;
-	} else if (t->tracking) {
-		pthread_mutex_t reentr_mutex_orig;
-		ast_reentrancy_lock(lt);
-		/* Restore lock tracking to what it was prior to the wait */
-		reentr_mutex_orig = lt->reentr_mutex;
-		*lt = lt_orig;
-		/* un-trash the mutex we just copied over */
-		lt->reentr_mutex = reentr_mutex_orig;
-		ast_reentrancy_unlock(lt);
-
+	} else if (lt) {
+		restore_lock_tracking(lt, &lt_orig);
 		ast_restore_lock_info(t);
 	}
 #endif /* DEBUG_THREADS */
@@ -593,32 +618,27 @@ int __ast_cond_timedwait(const char *filename, int lineno, const char *func,
 	int res;
 
 #ifdef DEBUG_THREADS
-	struct ast_lock_track *lt;
+	struct ast_lock_track *lt = NULL;
 	struct ast_lock_track lt_orig;
-	int canlog = strcmp(filename, "logger.c") & t->tracking;
+	int canlog = t->tracking && strcmp(filename, "logger.c");
 
 #if defined(AST_MUTEX_INIT_W_CONSTRUCTORS) && defined(CAN_COMPARE_MUTEX_TO_INIT_VALUE)
 	if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
 		__ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized.\n",
 				   filename, lineno, func, mutex_name);
-		res = __ast_pthread_mutex_init(t->tracking, filename, lineno, func, mutex_name, t);
-		if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
-			__ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized and unable to initialize.\n",
-					 filename, lineno, func, mutex_name);
-		}
-		return res;
+		DO_THREAD_CRASH;
+		return EINVAL;
 	}
 #endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
 
-	if (t->tracking && !t->track) {
-		ast_reentrancy_init(&t->track);
+	if (t->tracking) {
+		lt = ast_get_reentrancy(&t->track);
 	}
-	lt = t->track;
 
-	if (t->tracking) {
+	if (lt) {
 		ast_reentrancy_lock(lt);
 		if (lt->reentrancy && (lt->thread[ROFFSET] != pthread_self())) {
-			__ast_mutex_logger("%s line %d (%s): attempted unlock mutex '%s' without owning it!\n",
+			__ast_mutex_logger("%s line %d (%s): attempted wait using mutex '%s' without owning it!\n",
 					   filename, lineno, func, mutex_name);
 			__ast_mutex_logger("%s line %d (%s): '%s' was locked here.\n",
 					   lt->file[ROFFSET], lt->lineno[ROFFSET], lt->func[ROFFSET], mutex_name);
@@ -627,7 +647,7 @@ int __ast_cond_timedwait(const char *filename, int lineno, const char *func,
 #endif
 			DO_THREAD_CRASH;
 		} else if (lt->reentrancy <= 0) {
-			__ast_mutex_logger("%s line %d (%s): attempted to wait on an unlocked mutex '%s'\n",
+			__ast_mutex_logger("%s line %d (%s): attempted wait using an unlocked mutex '%s'\n",
 					   filename, lineno, func, mutex_name);
 			DO_THREAD_CRASH;
 		}
@@ -650,17 +670,9 @@ int __ast_cond_timedwait(const char *filename, int lineno, const char *func,
 		__ast_mutex_logger("%s line %d (%s): Error waiting on condition mutex '%s'\n",
 				   filename, lineno, func, strerror(res));
 		DO_THREAD_CRASH;
-	} else if (t->tracking) {
-		pthread_mutex_t reentr_mutex_orig;
-		ast_reentrancy_lock(lt);
-		/* Restore lock tracking to what it was prior to the wait */
-		reentr_mutex_orig = lt->reentr_mutex;
-		*lt = lt_orig;
-		/* un-trash the mutex we just copied over */
-		lt->reentr_mutex = reentr_mutex_orig;
-		ast_reentrancy_unlock(lt);
-
-		ast_suspend_lock_info(t);
+	} else if (lt) {
+		restore_lock_tracking(lt, &lt_orig);
+		ast_restore_lock_info(t);
 	}
 #endif /* DEBUG_THREADS */
 
@@ -673,30 +685,28 @@ int __ast_rwlock_init(int tracking, const char *filename, int lineno, const char
 	pthread_rwlockattr_t attr;
 
 #ifdef DEBUG_THREADS
-
 #if defined(AST_MUTEX_INIT_W_CONSTRUCTORS) && defined(CAN_COMPARE_MUTEX_TO_INIT_VALUE)
-	int canlog = strcmp(filename, "logger.c") & t->tracking;
-
 	if (t->lock != ((pthread_rwlock_t) __AST_RWLOCK_INIT_VALUE)) {
+		int canlog = tracking && strcmp(filename, "logger.c");
+
 		__ast_mutex_logger("%s line %d (%s): Warning: rwlock '%s' is already initialized.\n",
 				filename, lineno, func, rwlock_name);
-		return 0;
+		DO_THREAD_CRASH;
+		return EBUSY;
 	}
 #endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
 
-	if ((t->tracking = tracking)) {
-		ast_reentrancy_init(&t->track);
-	}
+	t->track = NULL;
+	t->tracking = tracking;
 #endif /* DEBUG_THREADS */
 
 	pthread_rwlockattr_init(&attr);
-
 #ifdef HAVE_PTHREAD_RWLOCK_PREFER_WRITER_NP
 	pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NP);
 #endif
-
 	res = pthread_rwlock_init(&t->lock, &attr);
 	pthread_rwlockattr_destroy(&attr);
+
 	return res;
 }
 
@@ -706,16 +716,17 @@ int __ast_rwlock_destroy(const char *filename, int lineno, const char *func, con
 
 #ifdef DEBUG_THREADS
 	struct ast_lock_track *lt = t->track;
-	int canlog = strcmp(filename, "logger.c") & t->tracking;
+	int canlog = t->tracking && strcmp(filename, "logger.c");
 
 #if defined(AST_MUTEX_INIT_W_CONSTRUCTORS) && defined(CAN_COMPARE_MUTEX_TO_INIT_VALUE)
 	if (t->lock == ((pthread_rwlock_t) __AST_RWLOCK_INIT_VALUE)) {
 		__ast_mutex_logger("%s line %d (%s): Warning: rwlock '%s' is uninitialized.\n",
 				   filename, lineno, func, rwlock_name);
-		return 0;
+		DO_THREAD_CRASH;
+		res = EINVAL;
+		goto lt_cleanup;
 	}
 #endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
-
 #endif /* DEBUG_THREADS */
 
 	res = pthread_rwlock_destroy(&t->lock);
@@ -725,7 +736,10 @@ int __ast_rwlock_destroy(const char *filename, int lineno, const char *func, con
 		__ast_mutex_logger("%s line %d (%s): Error destroying rwlock %s: %s\n",
 				filename, lineno, func, rwlock_name, strerror(res));
 	}
-	if (t->tracking && lt) {
+#if defined(AST_MUTEX_INIT_W_CONSTRUCTORS) && defined(CAN_COMPARE_MUTEX_TO_INIT_VALUE)
+lt_cleanup:
+#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
+	if (lt) {
 		ast_reentrancy_lock(lt);
 		lt->file[0] = filename;
 		lt->lineno[0] = lineno;
@@ -748,33 +762,27 @@ int __ast_rwlock_unlock(const char *filename, int line, const char *func, ast_rw
 	int res;
 
 #ifdef DEBUG_THREADS
-	struct ast_lock_track *lt;
-	int canlog = strcmp(filename, "logger.c") & t->tracking;
+	struct ast_lock_track *lt = NULL;
+	int canlog = t->tracking && strcmp(filename, "logger.c");
 #ifdef HAVE_BKTR
 	struct ast_bt *bt = NULL;
 #endif
 	int lock_found = 0;
 
-
 #if defined(AST_MUTEX_INIT_W_CONSTRUCTORS) && defined(CAN_COMPARE_MUTEX_TO_INIT_VALUE)
 	if ((t->lock) == ((pthread_rwlock_t) __AST_RWLOCK_INIT_VALUE)) {
 		__ast_mutex_logger("%s line %d (%s): Warning: rwlock '%s' is uninitialized.\n",
 				   filename, line, func, name);
-		res = __ast_rwlock_init(t->tracking, filename, line, func, name, t);
-		if ((t->lock) == ((pthread_rwlock_t) __AST_RWLOCK_INIT_VALUE)) {
-			__ast_mutex_logger("%s line %d (%s): Error: rwlock '%s' is uninitialized and unable to initialize.\n",
-					filename, line, func, name);
-		}
-		return res;
+		DO_THREAD_CRASH;
+		return EINVAL;
 	}
 #endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
 
-	if (t->tracking && !t->track) {
-		ast_reentrancy_init(&t->track);
+	if (t->tracking) {
+		lt = ast_get_reentrancy(&t->track);
 	}
-	lt = t->track;
 
-	if (t->tracking) {
+	if (lt) {
 		ast_reentrancy_lock(lt);
 		if (lt->reentrancy) {
 			int i;
@@ -834,33 +842,17 @@ int __ast_rwlock_rdlock(const char *filename, int line, const char *func, ast_rw
 	int res;
 
 #ifdef DEBUG_THREADS
-	struct ast_lock_track *lt;
-	int canlog = strcmp(filename, "logger.c") & t->tracking;
+	struct ast_lock_track *lt = NULL;
+	int canlog = t->tracking && strcmp(filename, "logger.c");
 #ifdef HAVE_BKTR
 	struct ast_bt *bt = NULL;
 #endif
 
-#if defined(AST_MUTEX_INIT_W_CONSTRUCTORS) && defined(CAN_COMPARE_MUTEX_TO_INIT_VALUE)
-	if ((t->lock) == ((pthread_rwlock_t) __AST_RWLOCK_INIT_VALUE)) {
-		 /* Don't warn abount uninitialized lock.
-		  * Simple try to initialize it.
-		  * May be not needed in linux system.
-		  */
-		res = __ast_rwlock_init(t->tracking, filename, line, func, name, t);
-		if ((t->lock) == ((pthread_rwlock_t) __AST_RWLOCK_INIT_VALUE)) {
-			__ast_mutex_logger("%s line %d (%s): Error: rwlock '%s' is uninitialized and unable to initialize.\n",
-					filename, line, func, name);
-			return res;
-		}
-	}
-#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
-
-	if (t->tracking && !t->track) {
-		ast_reentrancy_init(&t->track);
+	if (t->tracking) {
+		lt = ast_get_reentrancy(&t->track);
 	}
-	lt = t->track;
 
-	if (t->tracking) {
+	if (lt) {
 #ifdef HAVE_BKTR
 		struct ast_bt tmp;
 
@@ -870,7 +862,7 @@ int __ast_rwlock_rdlock(const char *filename, int line, const char *func, ast_rw
 		ast_bt_get_addresses(&tmp);
 
 		ast_reentrancy_lock(lt);
-		if (lt->reentrancy != AST_MAX_REENTRANCY) {
+		if (lt->reentrancy < AST_MAX_REENTRANCY) {
 			lt->backtrace[lt->reentrancy] = tmp;
 			bt = &lt->backtrace[lt->reentrancy];
 		}
@@ -894,7 +886,7 @@ int __ast_rwlock_rdlock(const char *filename, int line, const char *func, ast_rw
 				if (wait_time > reported_wait && (wait_time % 5) == 0) {
 					__ast_mutex_logger("%s line %d (%s): Deadlock? waited %d sec for readlock '%s'?\n",
 						filename, line, func, (int)wait_time, name);
-					if (t->tracking) {
+					if (lt) {
 						ast_reentrancy_lock(lt);
 #ifdef HAVE_BKTR
 						__dump_backtrace(&lt->backtrace[lt->reentrancy], canlog);
@@ -918,7 +910,7 @@ int __ast_rwlock_rdlock(const char *filename, int line, const char *func, ast_rw
 #endif /* !DETECT_DEADLOCKS || !DEBUG_THREADS */
 
 #ifdef DEBUG_THREADS
-	if (!res && t->tracking) {
+	if (!res && lt) {
 		ast_reentrancy_lock(lt);
 		if (lt->reentrancy < AST_MAX_REENTRANCY) {
 			lt->file[lt->reentrancy] = filename;
@@ -928,10 +920,8 @@ int __ast_rwlock_rdlock(const char *filename, int line, const char *func, ast_rw
 			lt->reentrancy++;
 		}
 		ast_reentrancy_unlock(lt);
-		if (t->tracking) {
-			ast_mark_lock_acquired(t);
-		}
-	} else if (t->tracking) {
+		ast_mark_lock_acquired(t);
+	} else if (lt) {
 #ifdef HAVE_BKTR
 		if (lt->reentrancy) {
 			ast_reentrancy_lock(lt);
@@ -961,33 +951,17 @@ int __ast_rwlock_wrlock(const char *filename, int line, const char *func, ast_rw
 	int res;
 
 #ifdef DEBUG_THREADS
-	struct ast_lock_track *lt;
-	int canlog = strcmp(filename, "logger.c") & t->tracking;
+	struct ast_lock_track *lt = NULL;
+	int canlog = t->tracking && strcmp(filename, "logger.c");
 #ifdef HAVE_BKTR
 	struct ast_bt *bt = NULL;
 #endif
 
-#if defined(AST_MUTEX_INIT_W_CONSTRUCTORS) && defined(CAN_COMPARE_MUTEX_TO_INIT_VALUE)
-	if ((t->lock) == ((pthread_rwlock_t) __AST_RWLOCK_INIT_VALUE)) {
-		 /* Don't warn abount uninitialized lock.
-		  * Simple try to initialize it.
-		  * May be not needed in linux system.
-		  */
-		res = __ast_rwlock_init(t->tracking, filename, line, func, name, t);
-		if ((t->lock) == ((pthread_rwlock_t) __AST_RWLOCK_INIT_VALUE)) {
-			__ast_mutex_logger("%s line %d (%s): Error: rwlock '%s' is uninitialized and unable to initialize.\n",
-					filename, line, func, name);
-			return res;
-		}
-	}
-#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
-
-	if (t->tracking && !t->track) {
-		ast_reentrancy_init(&t->track);
+	if (t->tracking) {
+		lt = ast_get_reentrancy(&t->track);
 	}
-	lt = t->track;
 
-	if (t->tracking) {
+	if (lt) {
 #ifdef HAVE_BKTR
 		struct ast_bt tmp;
 
@@ -997,7 +971,7 @@ int __ast_rwlock_wrlock(const char *filename, int line, const char *func, ast_rw
 		ast_bt_get_addresses(&tmp);
 
 		ast_reentrancy_lock(lt);
-		if (lt->reentrancy != AST_MAX_REENTRANCY) {
+		if (lt->reentrancy < AST_MAX_REENTRANCY) {
 			lt->backtrace[lt->reentrancy] = tmp;
 			bt = &lt->backtrace[lt->reentrancy];
 		}
@@ -1021,7 +995,7 @@ int __ast_rwlock_wrlock(const char *filename, int line, const char *func, ast_rw
 				if (wait_time > reported_wait && (wait_time % 5) == 0) {
 					__ast_mutex_logger("%s line %d (%s): Deadlock? waited %d sec for writelock '%s'?\n",
 						filename, line, func, (int)wait_time, name);
-					if (t->tracking) {
+					if (lt) {
 						ast_reentrancy_lock(lt);
 #ifdef HAVE_BKTR
 						__dump_backtrace(&lt->backtrace[lt->reentrancy], canlog);
@@ -1045,7 +1019,7 @@ int __ast_rwlock_wrlock(const char *filename, int line, const char *func, ast_rw
 #endif /* !DETECT_DEADLOCKS || !DEBUG_THREADS */
 
 #ifdef DEBUG_THREADS
-	if (!res && t->tracking) {
+	if (!res && lt) {
 		ast_reentrancy_lock(lt);
 		if (lt->reentrancy < AST_MAX_REENTRANCY) {
 			lt->file[lt->reentrancy] = filename;
@@ -1055,10 +1029,8 @@ int __ast_rwlock_wrlock(const char *filename, int line, const char *func, ast_rw
 			lt->reentrancy++;
 		}
 		ast_reentrancy_unlock(lt);
-		if (t->tracking) {
-			ast_mark_lock_acquired(t);
-		}
-	} else if (t->tracking) {
+		ast_mark_lock_acquired(t);
+	} else if (lt) {
 #ifdef HAVE_BKTR
 		if (lt->reentrancy) {
 			ast_reentrancy_lock(lt);
@@ -1067,13 +1039,9 @@ int __ast_rwlock_wrlock(const char *filename, int line, const char *func, ast_rw
 		} else {
 			bt = NULL;
 		}
-		if (t->tracking) {
-			ast_remove_lock_info(t, bt);
-		}
+		ast_remove_lock_info(t, bt);
 #else
-		if (t->tracking) {
-			ast_remove_lock_info(t);
-		}
+		ast_remove_lock_info(t);
 #endif
 	}
 	if (res) {
@@ -1092,33 +1060,17 @@ int __ast_rwlock_timedrdlock(const char *filename, int line, const char *func, a
 	int res;
 
 #ifdef DEBUG_THREADS
-	struct ast_lock_track *lt;
-	int canlog = strcmp(filename, "logger.c") & t->tracking;
+	struct ast_lock_track *lt = NULL;
+	int canlog = t->tracking && strcmp(filename, "logger.c");
 #ifdef HAVE_BKTR
 	struct ast_bt *bt = NULL;
 #endif
 
-#if defined(AST_MUTEX_INIT_W_CONSTRUCTORS) && defined(CAN_COMPARE_MUTEX_TO_INIT_VALUE)
-	if ((t->lock) == ((pthread_rwlock_t) __AST_RWLOCK_INIT_VALUE)) {
-		 /* Don't warn abount uninitialized lock.
-		  * Simple try to initialize it.
-		  * May be not needed in linux system.
-		  */
-		res = __ast_rwlock_init(t->tracking, filename, line, func, name, t);
-		if ((t->lock) == ((pthread_rwlock_t) __AST_RWLOCK_INIT_VALUE)) {
-			__ast_mutex_logger("%s line %d (%s): Error: rwlock '%s' is uninitialized and unable to initialize.\n",
-					filename, line, func, name);
-			return res;
-		}
-	}
-#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
-
-	if (t->tracking && !t->track) {
-		ast_reentrancy_init(&t->track);
+	if (t->tracking) {
+		lt = ast_get_reentrancy(&t->track);
 	}
-	lt = t->track;
 
-	if (t->tracking) {
+	if (lt) {
 #ifdef HAVE_BKTR
 		struct ast_bt tmp;
 
@@ -1128,7 +1080,7 @@ int __ast_rwlock_timedrdlock(const char *filename, int line, const char *func, a
 		ast_bt_get_addresses(&tmp);
 
 		ast_reentrancy_lock(lt);
-		if (lt->reentrancy != AST_MAX_REENTRANCY) {
+		if (lt->reentrancy < AST_MAX_REENTRANCY) {
 			lt->backtrace[lt->reentrancy] = tmp;
 			bt = &lt->backtrace[lt->reentrancy];
 		}
@@ -1160,7 +1112,7 @@ int __ast_rwlock_timedrdlock(const char *filename, int line, const char *func, a
 #endif
 
 #ifdef DEBUG_THREADS
-	if (!res && t->tracking) {
+	if (!res && lt) {
 		ast_reentrancy_lock(lt);
 		if (lt->reentrancy < AST_MAX_REENTRANCY) {
 			lt->file[lt->reentrancy] = filename;
@@ -1170,10 +1122,8 @@ int __ast_rwlock_timedrdlock(const char *filename, int line, const char *func, a
 			lt->reentrancy++;
 		}
 		ast_reentrancy_unlock(lt);
-		if (t->tracking) {
-			ast_mark_lock_acquired(t);
-		}
-	} else if (t->tracking) {
+		ast_mark_lock_acquired(t);
+	} else if (lt) {
 #ifdef HAVE_BKTR
 		if (lt->reentrancy) {
 			ast_reentrancy_lock(lt);
@@ -1203,33 +1153,17 @@ int __ast_rwlock_timedwrlock(const char *filename, int line, const char *func, a
 	int res;
 
 #ifdef DEBUG_THREADS
-	struct ast_lock_track *lt;
-	int canlog = strcmp(filename, "logger.c") & t->tracking;
+	struct ast_lock_track *lt = NULL;
+	int canlog = t->tracking && strcmp(filename, "logger.c");
 #ifdef HAVE_BKTR
 	struct ast_bt *bt = NULL;
 #endif
 
-#if defined(AST_MUTEX_INIT_W_CONSTRUCTORS) && defined(CAN_COMPARE_MUTEX_TO_INIT_VALUE)
-	if ((t->lock) == ((pthread_rwlock_t) __AST_RWLOCK_INIT_VALUE)) {
-		 /* Don't warn abount uninitialized lock.
-		  * Simple try to initialize it.
-		  * May be not needed in linux system.
-		  */
-		res = __ast_rwlock_init(t->tracking, filename, line, func, name, t);
-		if ((t->lock) == ((pthread_rwlock_t) __AST_RWLOCK_INIT_VALUE)) {
-			__ast_mutex_logger("%s line %d (%s): Error: rwlock '%s' is uninitialized and unable to initialize.\n",
-					filename, line, func, name);
-			return res;
-		}
-	}
-#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
-
-	if (t->tracking && !t->track) {
-		ast_reentrancy_init(&t->track);
+	if (t->tracking) {
+		lt = ast_get_reentrancy(&t->track);
 	}
-	lt = t->track;
 
-	if (t->tracking) {
+	if (lt) {
 #ifdef HAVE_BKTR
 		struct ast_bt tmp;
 
@@ -1239,7 +1173,7 @@ int __ast_rwlock_timedwrlock(const char *filename, int line, const char *func, a
 		ast_bt_get_addresses(&tmp);
 
 		ast_reentrancy_lock(lt);
-		if (lt->reentrancy != AST_MAX_REENTRANCY) {
+		if (lt->reentrancy < AST_MAX_REENTRANCY) {
 			lt->backtrace[lt->reentrancy] = tmp;
 			bt = &lt->backtrace[lt->reentrancy];
 		}
@@ -1271,7 +1205,7 @@ int __ast_rwlock_timedwrlock(const char *filename, int line, const char *func, a
 #endif
 
 #ifdef DEBUG_THREADS
-	if (!res && t->tracking) {
+	if (!res && lt) {
 		ast_reentrancy_lock(lt);
 		if (lt->reentrancy < AST_MAX_REENTRANCY) {
 			lt->file[lt->reentrancy] = filename;
@@ -1281,10 +1215,8 @@ int __ast_rwlock_timedwrlock(const char *filename, int line, const char *func, a
 			lt->reentrancy++;
 		}
 		ast_reentrancy_unlock(lt);
-		if (t->tracking) {
-			ast_mark_lock_acquired(t);
-		}
-	} else if (t->tracking) {
+		ast_mark_lock_acquired(t);
+	} else if (lt) {
 #ifdef HAVE_BKTR
 		if (lt->reentrancy) {
 			ast_reentrancy_lock(lt);
@@ -1293,13 +1225,9 @@ int __ast_rwlock_timedwrlock(const char *filename, int line, const char *func, a
 		} else {
 			bt = NULL;
 		}
-		if (t->tracking) {
-			ast_remove_lock_info(t, bt);
-		}
+		ast_remove_lock_info(t, bt);
 #else
-		if (t->tracking) {
-			ast_remove_lock_info(t);
-		}
+		ast_remove_lock_info(t);
 #endif
 	}
 	if (res) {
@@ -1317,33 +1245,16 @@ int __ast_rwlock_tryrdlock(const char *filename, int line, const char *func, ast
 	int res;
 
 #ifdef DEBUG_THREADS
-	struct ast_lock_track *lt;
+	struct ast_lock_track *lt = NULL;
 #ifdef HAVE_BKTR
 	struct ast_bt *bt = NULL;
 #endif
-#if defined(AST_MUTEX_INIT_W_CONSTRUCTORS) && defined(CAN_COMPARE_MUTEX_TO_INIT_VALUE)
-	int canlog = strcmp(filename, "logger.c") & t->tracking;
 
-	if ((t->lock) == ((pthread_rwlock_t) __AST_RWLOCK_INIT_VALUE)) {
-		 /* Don't warn abount uninitialized lock.
-		  * Simple try to initialize it.
-		  * May be not needed in linux system.
-		  */
-		res = __ast_rwlock_init(t->tracking, filename, line, func, name, t);
-		if ((t->lock) == ((pthread_rwlock_t) __AST_RWLOCK_INIT_VALUE)) {
-			__ast_mutex_logger("%s line %d (%s): Error: rwlock '%s' is uninitialized and unable to initialize.\n",
-					filename, line, func, name);
-			return res;
-		}
-	}
-#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
-
-	if (t->tracking && !t->track) {
-		ast_reentrancy_init(&t->track);
+	if (t->tracking) {
+		lt = ast_get_reentrancy(&t->track);
 	}
-	lt = t->track;
 
-	if (t->tracking) {
+	if (lt) {
 #ifdef HAVE_BKTR
 		struct ast_bt tmp;
 
@@ -1353,7 +1264,7 @@ int __ast_rwlock_tryrdlock(const char *filename, int line, const char *func, ast
 		ast_bt_get_addresses(&tmp);
 
 		ast_reentrancy_lock(lt);
-		if (lt->reentrancy != AST_MAX_REENTRANCY) {
+		if (lt->reentrancy < AST_MAX_REENTRANCY) {
 			lt->backtrace[lt->reentrancy] = tmp;
 			bt = &lt->backtrace[lt->reentrancy];
 		}
@@ -1369,7 +1280,7 @@ int __ast_rwlock_tryrdlock(const char *filename, int line, const char *func, ast
 	res = pthread_rwlock_tryrdlock(&t->lock);
 
 #ifdef DEBUG_THREADS
-	if (!res && t->tracking) {
+	if (!res && lt) {
 		ast_reentrancy_lock(lt);
 		if (lt->reentrancy < AST_MAX_REENTRANCY) {
 			lt->file[lt->reentrancy] = filename;
@@ -1379,10 +1290,8 @@ int __ast_rwlock_tryrdlock(const char *filename, int line, const char *func, ast
 			lt->reentrancy++;
 		}
 		ast_reentrancy_unlock(lt);
-		if (t->tracking) {
-			ast_mark_lock_acquired(t);
-		}
-	} else if (t->tracking) {
+		ast_mark_lock_acquired(t);
+	} else if (lt) {
 		ast_mark_lock_failed(t);
 	}
 #endif /* DEBUG_THREADS */
@@ -1395,33 +1304,16 @@ int __ast_rwlock_trywrlock(const char *filename, int line, const char *func, ast
 	int res;
 
 #ifdef DEBUG_THREADS
-	struct ast_lock_track *lt;
+	struct ast_lock_track *lt = NULL;
 #ifdef HAVE_BKTR
 	struct ast_bt *bt = NULL;
 #endif
-#if defined(AST_MUTEX_INIT_W_CONSTRUCTORS) && defined(CAN_COMPARE_MUTEX_TO_INIT_VALUE)
-	int canlog = strcmp(filename, "logger.c") & t->tracking;
-
-	if ((t->lock) == ((pthread_rwlock_t) __AST_RWLOCK_INIT_VALUE)) {
-		 /* Don't warn abount uninitialized lock.
-		  * Simple try to initialize it.
-		  * May be not needed in linux system.
-		  */
-		res = __ast_rwlock_init(t->tracking, filename, line, func, name, t);
-		if ((t->lock) == ((pthread_rwlock_t) __AST_RWLOCK_INIT_VALUE)) {
-			__ast_mutex_logger("%s line %d (%s): Error: rwlock '%s' is uninitialized and unable to initialize.\n",
-					filename, line, func, name);
-			return res;
-		}
-	}
-#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
 
-	if (t->tracking && !t->track) {
-		ast_reentrancy_init(&t->track);
+	if (t->tracking) {
+		lt = ast_get_reentrancy(&t->track);
 	}
-	lt = t->track;
 
-	if (t->tracking) {
+	if (lt) {
 #ifdef HAVE_BKTR
 		struct ast_bt tmp;
 
@@ -1431,7 +1323,7 @@ int __ast_rwlock_trywrlock(const char *filename, int line, const char *func, ast
 		ast_bt_get_addresses(&tmp);
 
 		ast_reentrancy_lock(lt);
-		if (lt->reentrancy != AST_MAX_REENTRANCY) {
+		if (lt->reentrancy < AST_MAX_REENTRANCY) {
 			lt->backtrace[lt->reentrancy] = tmp;
 			bt = &lt->backtrace[lt->reentrancy];
 		}
@@ -1447,7 +1339,7 @@ int __ast_rwlock_trywrlock(const char *filename, int line, const char *func, ast
 	res = pthread_rwlock_trywrlock(&t->lock);
 
 #ifdef DEBUG_THREADS
-	if (!res && t->tracking) {
+	if (!res && lt) {
 		ast_reentrancy_lock(lt);
 		if (lt->reentrancy < AST_MAX_REENTRANCY) {
 			lt->file[lt->reentrancy] = filename;
@@ -1458,7 +1350,7 @@ int __ast_rwlock_trywrlock(const char *filename, int line, const char *func, ast
 		}
 		ast_reentrancy_unlock(lt);
 		ast_mark_lock_acquired(t);
-	} else if (t->tracking) {
+	} else if (lt) {
 		ast_mark_lock_failed(t);
 	}
 #endif /* DEBUG_THREADS */
diff --git a/main/logger.c b/main/logger.c
index 31251ff..fb9e8ed 100644
--- a/main/logger.c
+++ b/main/logger.c
@@ -40,7 +40,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420899 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 /* When we include logger.h again it will trample on some stuff in syslog.h, but
  * nothing we care about in here. */
@@ -107,6 +107,7 @@ static struct {
 } logfiles = { 1 };
 
 static char hostname[MAXHOSTNAMELEN];
+AST_THREADSTORAGE_RAW(in_safe_log);
 
 enum logtypes {
 	LOGTYPE_SYSLOG,
@@ -241,8 +242,6 @@ AST_THREADSTORAGE(verbose_build_buf);
 AST_THREADSTORAGE(log_buf);
 #define LOG_BUF_INIT_SIZE       256
 
-static void logger_queue_init(void);
-
 static void make_components(struct logchannel *chan)
 {
 	char *w;
@@ -287,6 +286,70 @@ static void make_components(struct logchannel *chan)
 	chan->logmask = logmask;
 }
 
+/*!
+ * \brief create the filename that will be used for a logger channel.
+ *
+ * \param channel The name of the logger channel
+ * \param[out] filename The filename for the logger channel
+ * \param size The size of the filename buffer
+ */
+static void make_filename(const char *channel, char *filename, size_t size)
+{
+	const char *log_dir_prefix = "";
+	const char *log_dir_separator = "";
+
+	*filename = '\0';
+
+	if (!strcasecmp(channel, "console")) {
+		return;
+	}
+
+	if (!strncasecmp(channel, "syslog", 6)) {
+		ast_copy_string(filename, channel, size);
+		return;
+	}
+
+	/* It's a filename */
+
+	if (channel[0] != '/') {
+		log_dir_prefix = ast_config_AST_LOG_DIR;
+		log_dir_separator = "/";
+	}
+
+	if (!ast_strlen_zero(hostname)) {
+		snprintf(filename, size, "%s%s%s.%s",
+			log_dir_prefix, log_dir_separator, channel, hostname);
+	} else {
+		snprintf(filename, size, "%s%s%s",
+			log_dir_prefix, log_dir_separator, channel);
+	}
+}
+
+/*!
+ * \brief Find a particular logger channel by name
+ *
+ * \pre logchannels list is locked
+ *
+ * \param channel The name of the logger channel to find
+ * \retval non-NULL The corresponding logger channel
+ * \retval NULL Unable to find a logger channel with that particular name
+ */
+static struct logchannel *find_logchannel(const char *channel)
+{
+	char filename[PATH_MAX];
+	struct logchannel *chan;
+
+	make_filename(channel, filename, sizeof(filename));
+
+	AST_RWLIST_TRAVERSE(&logchannels, chan, list) {
+		if (!strcmp(chan->filename, filename)) {
+			return chan;
+		}
+	}
+
+	return NULL;
+}
+
 static struct logchannel *make_logchannel(const char *channel, const char *components, int lineno, int dynamic)
 {
 	struct logchannel *chan;
@@ -302,6 +365,8 @@ static struct logchannel *make_logchannel(const char *channel, const char *compo
 	chan->lineno = lineno;
 	chan->dynamic = dynamic;
 
+	make_filename(channel, chan->filename, sizeof(chan->filename));
+
 	if (!strcasecmp(channel, "console")) {
 		chan->type = LOGTYPE_CONSOLE;
 	} else if (!strncasecmp(channel, "syslog", 6)) {
@@ -323,25 +388,7 @@ static struct logchannel *make_logchannel(const char *channel, const char *compo
 		}
 
 		chan->type = LOGTYPE_SYSLOG;
-		ast_copy_string(chan->filename, channel, sizeof(chan->filename));
-		openlog("asterisk", LOG_PID, chan->facility);
 	} else {
-		const char *log_dir_prefix = "";
-		const char *log_dir_separator = "";
-
-		if (channel[0] != '/') {
-			log_dir_prefix = ast_config_AST_LOG_DIR;
-			log_dir_separator = "/";
-		}
-
-		if (!ast_strlen_zero(hostname)) {
-			snprintf(chan->filename, sizeof(chan->filename), "%s%s%s.%s",
-				log_dir_prefix, log_dir_separator, channel, hostname);
-		} else {
-			snprintf(chan->filename, sizeof(chan->filename), "%s%s%s",
-				log_dir_prefix, log_dir_separator, channel);
-		}
-
 		if (!(chan->fileptr = fopen(chan->filename, "a"))) {
 			/* Can't do real logging here since we're called with a lock
 			 * so log to any attached consoles */
@@ -369,7 +416,14 @@ static struct logchannel *make_logchannel(const char *channel, const char *compo
 	return chan;
 }
 
-static void init_logger_chain(int locked, const char *altconf)
+/* \brief Read config, setup channels.
+ * \param locked The logchannels list is locked and this is a reload
+ * \param altconf Alternate configuration file to read.
+ *
+ * \retval 0 Success
+ * \retval -1 No config found or Failed
+ */
+static int init_logger_chain(int locked, const char *altconf)
 {
 	struct logchannel *chan;
 	struct ast_config *cfg;
@@ -377,16 +431,25 @@ static void init_logger_chain(int locked, const char *altconf)
 	const char *s;
 	struct ast_flags config_flags = { 0 };
 
-	display_callids = 1;
-
 	if (!(cfg = ast_config_load2(S_OR(altconf, "logger.conf"), "logger", config_flags)) || cfg == CONFIG_STATUS_FILEINVALID) {
-		return;
+		cfg = NULL;
 	}
 
-	/* delete our list of log channels */
 	if (!locked) {
 		AST_RWLIST_WRLOCK(&logchannels);
 	}
+
+	/* Set defaults */
+	hostname[0] = '\0';
+	display_callids = 1;
+	memset(&logfiles, 0, sizeof(logfiles));
+	logfiles.queue_log = 1;
+	ast_copy_string(dateformat, "%b %e %T", sizeof(dateformat));
+	ast_copy_string(queue_log_name, QUEUELOG, sizeof(queue_log_name));
+	exec_after_rotate[0] = '\0';
+	rotatestrategy = SEQUENTIAL;
+
+	/* delete our list of log channels */
 	while ((chan = AST_RWLIST_REMOVE_HEAD(&logchannels, list))) {
 		ast_free(chan);
 	}
@@ -401,16 +464,13 @@ static void init_logger_chain(int locked, const char *altconf)
 
 	/* If no config file, we're fine, set default options. */
 	if (!cfg) {
-		if (errno) {
-			fprintf(stderr, "Unable to open logger.conf: %s; default settings will be used.\n", strerror(errno));
-		} else {
-			fprintf(stderr, "Errors detected in logger.conf: see above; default settings will be used.\n");
-		}
 		if (!(chan = ast_calloc(1, sizeof(*chan)))) {
-			return;
+			fprintf(stderr, "Failed to initialize default logging\n");
+			return -1;
 		}
 		chan->type = LOGTYPE_CONSOLE;
 		chan->logmask = __LOG_WARNING | __LOG_NOTICE | __LOG_ERROR;
+
 		if (!locked) {
 			AST_RWLIST_WRLOCK(&logchannels);
 		}
@@ -419,7 +479,8 @@ static void init_logger_chain(int locked, const char *altconf)
 		if (!locked) {
 			AST_RWLIST_UNLOCK(&logchannels);
 		}
-		return;
+
+		return -1;
 	}
 
 	if ((s = ast_variable_retrieve(cfg, "general", "appendhostname"))) {
@@ -428,17 +489,14 @@ static void init_logger_chain(int locked, const char *altconf)
 				ast_copy_string(hostname, "unknown", sizeof(hostname));
 				fprintf(stderr, "What box has no hostname???\n");
 			}
-		} else
-			hostname[0] = '\0';
-	} else
-		hostname[0] = '\0';
+		}
+	}
 	if ((s = ast_variable_retrieve(cfg, "general", "display_callids"))) {
 		display_callids = ast_true(s);
 	}
-	if ((s = ast_variable_retrieve(cfg, "general", "dateformat")))
+	if ((s = ast_variable_retrieve(cfg, "general", "dateformat"))) {
 		ast_copy_string(dateformat, s, sizeof(dateformat));
-	else
-		ast_copy_string(dateformat, "%b %e %T", sizeof(dateformat));
+	}
 	if ((s = ast_variable_retrieve(cfg, "general", "queue_log"))) {
 		logfiles.queue_log = ast_true(s);
 	}
@@ -500,6 +558,8 @@ static void init_logger_chain(int locked, const char *altconf)
 	}
 
 	ast_config_destroy(cfg);
+
+	return 0;
 }
 
 void ast_child_verbose(int level, const char *fmt, ...)
@@ -560,20 +620,8 @@ void ast_queue_log(const char *queuename, const char *callid, const char *agent,
 		return;
 	}
 	if (!queuelog_init) {
-		AST_RWLIST_WRLOCK(&logchannels);
-		if (!queuelog_init) {
-			/*
-			 * We have delayed initializing the queue logging system so
-			 * preloaded realtime modules can get up.  We must initialize
-			 * now since someone is trying to log something.
-			 */
-			logger_queue_init();
-			queuelog_init = 1;
-			AST_RWLIST_UNLOCK(&logchannels);
-			ast_queue_log("NONE", "NONE", "NONE", "QUEUESTART", "%s", "");
-		} else {
-			AST_RWLIST_UNLOCK(&logchannels);
-		}
+		/* We must initialize now since someone is trying to log something. */
+		logger_queue_start();
 	}
 
 	if (ast_check_realtime("queue_log")) {
@@ -933,6 +981,41 @@ int ast_logger_rotate()
 	return reload_logger(1, NULL);
 }
 
+int ast_logger_rotate_channel(const char *log_channel)
+{
+	struct logchannel *f;
+	int success = AST_LOGGER_FAILURE;
+	char filename[PATH_MAX];
+
+	make_filename(log_channel, filename, sizeof(filename));
+
+	AST_RWLIST_WRLOCK(&logchannels);
+
+	ast_mkdir(ast_config_AST_LOG_DIR, 0644);
+
+	AST_RWLIST_TRAVERSE(&logchannels, f, list) {
+		if (f->disabled) {
+			f->disabled = 0;	/* Re-enable logging at reload */
+			manager_event(EVENT_FLAG_SYSTEM, "LogChannel", "Channel: %s\r\nEnabled: Yes\r\n",
+				f->filename);
+		}
+		if (f->fileptr && (f->fileptr != stdout) && (f->fileptr != stderr)) {
+			fclose(f->fileptr);	/* Close file */
+			f->fileptr = NULL;
+			if (strcmp(filename, f->filename) == 0) {
+				rotate_file(f->filename);
+				success = AST_LOGGER_SUCCESS;
+			}
+		}
+	}
+
+	init_logger_chain(1 /* locked */, NULL);
+
+	AST_RWLIST_UNLOCK(&logchannels);
+
+	return success;
+}
+
 static char *handle_logger_set_level(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
 	int x;
@@ -975,6 +1058,48 @@ static char *handle_logger_set_level(struct ast_cli_entry *e, int cmd, struct as
 	return CLI_SUCCESS;
 }
 
+int ast_logger_get_channels(int (*logentry)(const char *channel, const char *type,
+	const char *status, const char *configuration, void *data), void *data)
+{
+	struct logchannel *chan;
+	struct ast_str *configs = ast_str_create(64);
+	int res = AST_LOGGER_SUCCESS;
+
+	if (!configs) {
+		return AST_LOGGER_ALLOC_ERROR;
+	}
+
+	AST_RWLIST_RDLOCK(&logchannels);
+	AST_RWLIST_TRAVERSE(&logchannels, chan, list) {
+		unsigned int level;
+
+		ast_str_reset(configs);
+
+		for (level = 0; level < ARRAY_LEN(levels); level++) {
+			if ((chan->logmask & (1 << level)) && levels[level]) {
+				ast_str_append(&configs, 0, "%s ", levels[level]);
+			}
+		}
+
+		res = logentry(chan->filename, chan->type == LOGTYPE_CONSOLE ? "Console" :
+			(chan->type == LOGTYPE_SYSLOG ? "Syslog" : "File"), chan->disabled ?
+			"Disabled" : "Enabled", ast_str_buffer(configs), data);
+
+		if (res) {
+			AST_RWLIST_UNLOCK(&logchannels);
+			ast_free(configs);
+			configs = NULL;
+			return AST_LOGGER_FAILURE;
+		}
+	}
+	AST_RWLIST_UNLOCK(&logchannels);
+
+	ast_free(configs);
+	configs = NULL;
+
+	return AST_LOGGER_SUCCESS;
+}
+
 /*! \brief CLI command to show logging system configuration */
 static char *handle_logger_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
@@ -1014,10 +1139,38 @@ static char *handle_logger_show_channels(struct ast_cli_entry *e, int cmd, struc
 	return CLI_SUCCESS;
 }
 
-static char *handle_logger_add_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+int ast_logger_create_channel(const char *log_channel, const char *components)
 {
 	struct logchannel *chan;
 
+	if (ast_strlen_zero(components)) {
+		return AST_LOGGER_DECLINE;
+	}
+
+	AST_RWLIST_WRLOCK(&logchannels);
+
+	chan = find_logchannel(log_channel);
+	if (chan) {
+		AST_RWLIST_UNLOCK(&logchannels);
+		return AST_LOGGER_FAILURE;
+	}
+
+	chan = make_logchannel(log_channel, components, 0, 1);
+	if (!chan) {
+		AST_RWLIST_UNLOCK(&logchannels);
+		return AST_LOGGER_ALLOC_ERROR;
+	}
+
+	AST_RWLIST_INSERT_HEAD(&logchannels, chan, list);
+	global_logmask |= chan->logmask;
+
+	AST_RWLIST_UNLOCK(&logchannels);
+
+	return AST_LOGGER_SUCCESS;
+}
+
+static char *handle_logger_add_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
 	switch (cmd) {
 	case CLI_INIT:
 		e->command = "logger add channel";
@@ -1036,31 +1189,43 @@ static char *handle_logger_add_channel(struct ast_cli_entry *e, int cmd, struct
 		return CLI_SHOWUSAGE;
 	}
 
-	AST_RWLIST_WRLOCK(&logchannels);
-	AST_RWLIST_TRAVERSE(&logchannels, chan, list) {
-		if (!strcmp(chan->filename, a->argv[3])) {
-			break;
-		}
-	}
-
-	if (chan) {
-		AST_RWLIST_UNLOCK(&logchannels);
+	switch (ast_logger_create_channel(a->argv[3], a->argv[4])) {
+	case AST_LOGGER_SUCCESS:
+		return CLI_SUCCESS;
+	case AST_LOGGER_FAILURE:
 		ast_cli(a->fd, "Logger channel '%s' already exists\n", a->argv[3]);
 		return CLI_SUCCESS;
+	case AST_LOGGER_DECLINE:
+	case AST_LOGGER_ALLOC_ERROR:
+	default:
+		ast_cli(a->fd, "ERROR: Unable to create log channel '%s'\n", a->argv[3]);
+		return CLI_FAILURE;
 	}
+}
 
-	chan = make_logchannel(a->argv[3], a->argv[4], 0, 1);
-	if (chan) {
-		AST_RWLIST_INSERT_HEAD(&logchannels, chan, list);
-		global_logmask |= chan->logmask;
+int ast_logger_remove_channel(const char *log_channel)
+{
+	struct logchannel *chan;
+
+	AST_RWLIST_WRLOCK(&logchannels);
+
+	chan = find_logchannel(log_channel);
+	if (chan && chan->dynamic) {
+		AST_RWLIST_REMOVE(&logchannels, chan, list);
+	} else {
 		AST_RWLIST_UNLOCK(&logchannels);
-		return CLI_SUCCESS;
+		return AST_LOGGER_FAILURE;
 	}
-
 	AST_RWLIST_UNLOCK(&logchannels);
-	ast_cli(a->fd, "ERROR: Unable to create log channel '%s'\n", a->argv[3]);
 
-	return CLI_FAILURE;
+	if (chan->fileptr) {
+		fclose(chan->fileptr);
+		chan->fileptr = NULL;
+	}
+	ast_free(chan);
+	chan = NULL;
+
+	return AST_LOGGER_SUCCESS;
 }
 
 static char *handle_logger_remove_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
@@ -1099,30 +1264,17 @@ static char *handle_logger_remove_channel(struct ast_cli_entry *e, int cmd, stru
 		return CLI_SHOWUSAGE;
 	}
 
-	AST_RWLIST_WRLOCK(&logchannels);
-	AST_RWLIST_TRAVERSE_SAFE_BEGIN(&logchannels, chan, list) {
-		if (chan->dynamic && !strcmp(chan->filename, a->argv[3])) {
-			AST_RWLIST_REMOVE_CURRENT(list);
-			break;
-		}
-	}
-	AST_RWLIST_TRAVERSE_SAFE_END;
-	AST_RWLIST_UNLOCK(&logchannels);
-
-	if (!chan) {
+	switch (ast_logger_remove_channel(a->argv[3])) {
+	case AST_LOGGER_SUCCESS:
+		ast_cli(a->fd, "Removed dynamic logger channel '%s'\n", a->argv[3]);
+		return CLI_SUCCESS;
+	case AST_LOGGER_FAILURE:
 		ast_cli(a->fd, "Unable to find dynamic logger channel '%s'\n", a->argv[3]);
 		return CLI_SUCCESS;
+	default:
+		ast_cli(a->fd, "Internal failure attempting to delete dynamic logger channel '%s'\n", a->argv[3]);
+		return CLI_FAILURE;
 	}
-
-	ast_cli(a->fd, "Removed dynamic logger channel '%s'\n", chan->filename);
-	if (chan->fileptr) {
-		fclose(chan->fileptr);
-		chan->fileptr = NULL;
-	}
-	ast_free(chan);
-	chan = NULL;
-
-	return CLI_SUCCESS;
 }
 
 struct verb {
@@ -1152,7 +1304,7 @@ static struct sigaction handle_SIGXFSZ = {
 	.sa_flags = SA_RESTART,
 };
 
-static void ast_log_vsyslog(struct logmsg *msg)
+static void ast_log_vsyslog(struct logmsg *msg, int facility)
 {
 	char buf[BUFSIZ];
 	int syslog_level = ast_syslog_priority_from_loglevel(msg->level);
@@ -1170,6 +1322,8 @@ static void ast_log_vsyslog(struct logmsg *msg)
 		return;
 	}
 
+	syslog_level = LOG_MAKEPRI(facility, syslog_level);
+
 	snprintf(buf, sizeof(buf), "%s[%d]%s: %s:%d in %s: %s",
 		 levels[msg->level], msg->lwp, call_identifier_str, msg->file, msg->line, msg->function, msg->message);
 
@@ -1255,7 +1409,7 @@ static void logger_print_normal(struct logmsg *logmsg)
 
 			/* Check syslog channels */
 			if (chan->type == LOGTYPE_SYSLOG && (chan->logmask & (1 << logmsg->level))) {
-				ast_log_vsyslog(logmsg);
+				ast_log_vsyslog(logmsg, chan->facility);
 			/* Console channels */
 			} else if (chan->type == LOGTYPE_CONSOLE && (chan->logmask & (1 << logmsg->level))) {
 				char linestr[128];
@@ -1397,8 +1551,33 @@ static void logger_queue_init(void)
 	}
 }
 
+/*!
+ * \brief Start the ast_queue_log() logger.
+ *
+ * \note Called when the system is fully booted after startup
+ * so preloaded realtime modules can get up.
+ *
+ * \return Nothing
+ */
+void logger_queue_start(void)
+{
+	/* Must not be called before the logger is initialized. */
+	ast_assert(logger_initialized);
+
+	AST_RWLIST_WRLOCK(&logchannels);
+	if (!queuelog_init) {
+		logger_queue_init();
+		queuelog_init = 1;
+		AST_RWLIST_UNLOCK(&logchannels);
+		ast_queue_log("NONE", "NONE", "NONE", "QUEUESTART", "%s", "");
+	} else {
+		AST_RWLIST_UNLOCK(&logchannels);
+	}
+}
+
 int init_logger(void)
 {
+	int res;
 	/* auto rotate if sig SIGXFSZ comes a-knockin */
 	sigaction(SIGXFSZ, &handle_SIGXFSZ, NULL);
 
@@ -1424,9 +1603,12 @@ int init_logger(void)
 	ast_mkdir(ast_config_AST_LOG_DIR, 0777);
 
 	/* create log channels */
-	init_logger_chain(0 /* locked */, NULL);
+	res = init_logger_chain(0 /* locked */, NULL);
 	ast_verb_update();
 	logger_initialized = 1;
+	if (res) {
+		ast_log(LOG_ERROR, "Errors detected in logger.conf.  Default console logging is being used.\n");
+	}
 
 	return 0;
 }
@@ -1745,6 +1927,36 @@ void ast_log(int level, const char *file, int line, const char *function, const
 	}
 }
 
+void ast_log_safe(int level, const char *file, int line, const char *function, const char *fmt, ...)
+{
+	va_list ap;
+	void *recursed = ast_threadstorage_get_ptr(&in_safe_log);
+	struct ast_callid *callid;
+
+	if (recursed) {
+		return;
+	}
+
+	if (ast_threadstorage_set_ptr(&in_safe_log, (void*)1)) {
+		/* We've failed to set the flag that protects against
+		 * recursion, so bail. */
+		return;
+	}
+
+	callid = ast_read_threadstorage_callid();
+
+	va_start(ap, fmt);
+	ast_log_full(level, file, line, function, callid, fmt, ap);
+	va_end(ap);
+
+	if (callid) {
+		ast_callid_unref(callid);
+	}
+
+	/* Clear flag so the next allocation failure can be logged. */
+	ast_threadstorage_set_ptr(&in_safe_log, NULL);
+}
+
 void ast_log_callid(int level, const char *file, int line, const char *function, struct ast_callid *callid, const char *fmt, ...)
 {
 	va_list ap;
diff --git a/main/manager.c b/main/manager.c
index c6eb56a..99c5502 100644
--- a/main/manager.c
+++ b/main/manager.c
@@ -54,9 +54,8 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428946 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
-#include "asterisk/_private.h"
 #include "asterisk/paths.h"	/* use various ast_config_AST_* */
 #include <ctype.h>
 #include <sys/time.h>
@@ -413,6 +412,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428946 $")
 			<parameter name="Reload">
 				<para>Whether or not a reload should take place (or name of specific module).</para>
 			</parameter>
+			<parameter name="PreserveEffectiveContext">
+				<para>Whether the effective category contents should be preserved on template change. Default is true (pre 13.2 behavior).</para>
+			</parameter>
 			<parameter name="Action-000000">
 				<para>Action to take.</para>
 				<para>0's represent 6 digit number beginning with 000000.</para>
@@ -2809,6 +2811,7 @@ AST_THREADSTORAGE(userevent_buf);
  */
 void astman_append(struct mansession *s, const char *fmt, ...)
 {
+	int res;
 	va_list ap;
 	struct ast_str *buf;
 
@@ -2817,8 +2820,11 @@ void astman_append(struct mansession *s, const char *fmt, ...)
 	}
 
 	va_start(ap, fmt);
-	ast_str_set_va(&buf, 0, fmt, ap);
+	res = ast_str_set_va(&buf, 0, fmt, ap);
 	va_end(ap);
+	if (res == AST_DYNSTR_BUILD_FAILED) {
+		return;
+	}
 
 	if (s->f != NULL || s->session->f != NULL) {
 		send_string(s, ast_str_buffer(buf));
@@ -2878,6 +2884,7 @@ void astman_send_error(struct mansession *s, const struct message *m, char *erro
 
 void astman_send_error_va(struct mansession *s, const struct message *m, const char *fmt, ...)
 {
+	int res;
 	va_list ap;
 	struct ast_str *buf;
 	char *msg;
@@ -2887,8 +2894,11 @@ void astman_send_error_va(struct mansession *s, const struct message *m, const c
 	}
 
 	va_start(ap, fmt);
-	ast_str_set_va(&buf, 0, fmt, ap);
+	res = ast_str_set_va(&buf, 0, fmt, ap);
 	va_end(ap);
+	if (res == AST_DYNSTR_BUILD_FAILED) {
+		return;
+	}
 
 	/* astman_append will use the same underlying buffer, so copy the message out
 	 * before sending the response */
@@ -2914,6 +2924,25 @@ void astman_send_listack(struct mansession *s, const struct message *m, char *ms
 	astman_send_response_full(s, m, "Success", msg, listflag);
 }
 
+void astman_send_list_complete_start(struct mansession *s, const struct message *m, const char *event_name, int count)
+{
+	const char *id = astman_get_header(m, "ActionID");
+
+	astman_append(s, "Event: %s\r\n", event_name);
+	if (!ast_strlen_zero(id)) {
+		astman_append(s, "ActionID: %s\r\n", id);
+	}
+	astman_append(s,
+		"EventList: Complete\r\n"
+		"ListItems: %d\r\n",
+		count);
+}
+
+void astman_send_list_complete_end(struct mansession *s)
+{
+	astman_append(s, "\r\n");
+}
+
 /*! \brief Lock the 'mansession' structure. */
 static void mansession_lock(struct mansession *s)
 {
@@ -3205,7 +3234,7 @@ static int authenticate(struct mansession *s, const struct message *m)
 			MD5Update(&md5, (unsigned char *) user->secret, strlen(user->secret));
 			MD5Final(digest, &md5);
 			for (x = 0; x < 16; x++)
-				len += sprintf(md5key + len, "%2.2x", (unsigned)digest[x]);
+				len += sprintf(md5key + len, "%02hhx", digest[x]);
 			if (!strcmp(md5key, key)) {
 				error = 0;
 			} else {
@@ -3448,18 +3477,18 @@ static int action_getconfigjson(struct mansession *s, const struct message *m)
 		category_name = ast_category_get_name(cur_category);
 		astman_append(s, "%s\"", comma1 ? "," : "");
 		astman_append_json(s, category_name);
-		astman_append(s, "\":[");
+		astman_append(s, "\":{");
 		comma1 = 1;
 
 		if (ast_category_is_template(cur_category)) {
-			astman_append(s, "istemplate:1");
+			astman_append(s, "\"istemplate\":1");
 			comma2 = 1;
 		}
 
 		if ((templates = ast_category_get_templates(cur_category))
 			&& ast_str_strlen(templates) > 0) {
 			astman_append(s, "%s", comma2 ? "," : "");
-			astman_append(s, "templates:\"%s\"", ast_str_buffer(templates));
+			astman_append(s, "\"templates\":\"%s\"", ast_str_buffer(templates));
 			ast_free(templates);
 			comma2 = 1;
 		}
@@ -3473,7 +3502,7 @@ static int action_getconfigjson(struct mansession *s, const struct message *m)
 			comma2 = 1;
 		}
 
-		astman_append(s, "]");
+		astman_append(s, "}");
 	}
 	astman_append(s, "}\r\n\r\n");
 
@@ -3599,7 +3628,10 @@ static enum error_type handle_updates(struct mansession *s, const struct message
 			if (inherit) {
 				while ((tmpl_name = ast_strsep(&inherit, ',', AST_STRSEP_STRIP))) {
 					if ((template = ast_category_get(cfg, tmpl_name, "TEMPLATES=restrict"))) {
-						ast_category_inherit(category, template);
+						if (ast_category_inherit(category, template)) {
+							result = FAILURE_ALLOCATION;
+							break;
+						}
 					} else {
 						ast_category_destroy(category);
 						category = NULL;
@@ -3768,6 +3800,8 @@ static int action_updateconfig(struct mansession *s, const struct message *m)
 	const char *dfn = astman_get_header(m, "DstFilename");
 	int res;
 	const char *rld = astman_get_header(m, "Reload");
+	int preserve_effective_context = CONFIG_SAVE_FLAG_PRESERVE_EFFECTIVE_CONTEXT;
+	const char *preserve_effective_context_string = astman_get_header(m, "PreserveEffectiveContext");
 	struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
 	enum error_type result;
 
@@ -3785,7 +3819,10 @@ static int action_updateconfig(struct mansession *s, const struct message *m)
 	result = handle_updates(s, m, cfg, dfn);
 	if (!result) {
 		ast_include_rename(cfg, sfn, dfn); /* change the include references from dfn to sfn, so things match up */
-		res = ast_config_text_file_save(dfn, cfg, "Manager");
+		if (!ast_strlen_zero(preserve_effective_context_string) && !ast_true(preserve_effective_context_string)) {
+			preserve_effective_context = CONFIG_SAVE_FLAG_NONE;
+		}
+		res = ast_config_text_file_save2(dfn, cfg, "Manager", preserve_effective_context);
 		ast_config_destroy(cfg);
 		if (res) {
 			astman_send_error(s, m, "Save of config failed");
@@ -4199,12 +4236,8 @@ static int action_hangup(struct mansession *s, const struct message *m)
 	regfree(&regexbuf);
 	ast_free(regex_string);
 
-	astman_append(s,
-		"Event: ChannelsHungupListComplete\r\n"
-		"EventList: Complete\r\n"
-		"ListItems: %d\r\n"
-		"%s"
-		"\r\n", channels_matched, idText);
+	astman_send_list_complete_start(s, m, "ChannelsHungupListComplete", channels_matched);
+	astman_send_list_complete_end(s);
 
 	return 0;
 }
@@ -4306,7 +4339,7 @@ static int action_status(struct mansession *s, const struct message *m)
 	struct ast_str *write_transpath = ast_str_alloca(256);
 	struct ast_str *read_transpath = ast_str_alloca(256);
 	struct ast_channel *chan;
-	struct ast_str *codec_buf = ast_str_alloca(64);
+	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 	int channels = 0;
 	int all = ast_strlen_zero(name); /* set if we want all channels */
 	char id_text[256];
@@ -4342,7 +4375,7 @@ static int action_status(struct mansession *s, const struct message *m)
 		}
 	}
 
-	astman_send_ack(s, m, "Channel status will follow");
+	astman_send_listack(s, m, "Channel status will follow", "start");
 
 	if (!ast_strlen_zero(id)) {
 		snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
@@ -4439,7 +4472,7 @@ static int action_status(struct mansession *s, const struct message *m)
 			S_OR(ast_channel_dialed(chan)->number.str, ""),
 			S_COR(ast_channel_connected_effective_id(chan).number.valid, ast_channel_connected_effective_id(chan).number.str, "<unknown>"),
 			S_COR(ast_channel_connected_effective_id(chan).name.valid, ast_channel_connected_effective_id(chan).name.str, "<unknown>"),
-			ast_channel_whentohangup(chan)->tv_sec,
+			(long)ast_channel_whentohangup(chan)->tv_sec,
 			bridge ? bridge->uniqueid : "",
 			ast_channel_linkedid(chan),
 			ast_channel_appl(chan),
@@ -4465,11 +4498,9 @@ static int action_status(struct mansession *s, const struct message *m)
 		ast_channel_iterator_destroy(it_chans);
 	}
 
-	astman_append(s,
-		"Event: StatusComplete\r\n"
-		"%s"
-		"Items: %d\r\n"
-		"\r\n", id_text, channels);
+	astman_send_list_complete_start(s, m, "StatusComplete", channels);
+	astman_append(s, "Items: %d\r\n", channels);
+	astman_send_list_complete_end(s);
 
 	ast_free(variable_str);
 
@@ -4527,6 +4558,8 @@ static int action_redirect(struct mansession *s, const struct message *m)
 	int pi = 0;
 	int pi2 = 0;
 	int res;
+	int chan1_wait = 0;
+	int chan2_wait = 0;
 
 	if (ast_strlen_zero(name)) {
 		astman_send_error(s, m, "Channel not specified");
@@ -4611,16 +4644,20 @@ static int action_redirect(struct mansession *s, const struct message *m)
 	}
 
 	/* Dual channel redirect in progress. */
-	if (ast_channel_pbx(chan)) {
-		ast_channel_lock(chan);
+	ast_channel_lock(chan);
+	if (ast_channel_is_bridged(chan)) {
 		ast_set_flag(ast_channel_flags(chan), AST_FLAG_BRIDGE_DUAL_REDIRECT_WAIT);
-		ast_channel_unlock(chan);
+		chan1_wait = 1;
 	}
-	if (ast_channel_pbx(chan2)) {
-		ast_channel_lock(chan2);
+	ast_channel_unlock(chan);
+
+	ast_channel_lock(chan2);
+	if (ast_channel_is_bridged(chan2)) {
 		ast_set_flag(ast_channel_flags(chan2), AST_FLAG_BRIDGE_DUAL_REDIRECT_WAIT);
-		ast_channel_unlock(chan2);
+		chan2_wait = 1;
 	}
+	ast_channel_unlock(chan2);
+
 	res = ast_async_goto(chan, context, exten, pi);
 	if (!res) {
 		if (!ast_strlen_zero(context2)) {
@@ -4638,12 +4675,12 @@ static int action_redirect(struct mansession *s, const struct message *m)
 	}
 
 	/* Release the bridge wait. */
-	if (ast_channel_pbx(chan)) {
+	if (chan1_wait) {
 		ast_channel_lock(chan);
 		ast_clear_flag(ast_channel_flags(chan), AST_FLAG_BRIDGE_DUAL_REDIRECT_WAIT);
 		ast_channel_unlock(chan);
 	}
-	if (ast_channel_pbx(chan2)) {
+	if (chan2_wait) {
 		ast_channel_lock(chan2);
 		ast_clear_flag(ast_channel_flags(chan2), AST_FLAG_BRIDGE_DUAL_REDIRECT_WAIT);
 		ast_channel_unlock(chan2);
@@ -4756,7 +4793,7 @@ static int action_atxfer(struct mansession *s, const struct message *m)
 static int check_blacklist(const char *cmd)
 {
 	char *cmd_copy, *cur_cmd;
-	char *cmd_words[MAX_BLACKLIST_CMD_LEN] = { NULL, };
+	char *cmd_words[AST_MAX_CMD_LEN] = { NULL, };
 	int i;
 
 	cmd_copy = ast_strdupa(cmd);
@@ -4926,8 +4963,6 @@ static void *fast_originate(void *data)
 			S_OR(in->cid_name, NULL),
 			in->vars, in->account, &chan, in->early_media, &assignedids);
 	}
-	/* Any vars memory was passed to the ast_pbx_outgoing_xxx() calls. */
-	in->vars = NULL;
 
 	if (!chan) {
 		snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);
@@ -5374,11 +5409,11 @@ static int action_originate(struct mansession *s, const struct message *m)
 		}
 	} else if (!ast_strlen_zero(app)) {
 		res = ast_pbx_outgoing_app(tech, cap, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL, assignedids.uniqueid ? &assignedids : NULL);
-		/* Any vars memory was passed to ast_pbx_outgoing_app(). */
+		ast_variables_destroy(vars);
 	} else {
 		if (exten && context && pi) {
 			res = ast_pbx_outgoing_exten(tech, cap, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL, bridge_early, assignedids.uniqueid ? &assignedids : NULL);
-			/* Any vars memory was passed to ast_pbx_outgoing_exten(). */
+			ast_variables_destroy(vars);
 		} else {
 			astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
 			ast_variables_destroy(vars);
@@ -5908,12 +5943,8 @@ static int action_coreshowchannels(struct mansession *s, const struct message *m
 	}
 	ao2_iterator_destroy(&it_chans);
 
-	astman_append(s,
-		"Event: CoreShowChannelsComplete\r\n"
-		"EventList: Complete\r\n"
-		"ListItems: %d\r\n"
-		"%s"
-		"\r\n", numchans, idText);
+	astman_send_list_complete_start(s, m, "CoreShowChannelsComplete", numchans);
+	astman_send_list_complete_end(s);
 
 	return 0;
 }
@@ -5938,9 +5969,6 @@ static int manager_modulecheck(struct mansession *s, const struct message *m)
 	const char *module = astman_get_header(m, "Module");
 	const char *id = astman_get_header(m, "ActionID");
 	char idText[256];
-#if !defined(LOW_MEMORY)
-	const char *version;
-#endif
 	char filename[PATH_MAX];
 	char *cut;
 
@@ -5957,11 +5985,6 @@ static int manager_modulecheck(struct mansession *s, const struct message *m)
 		astman_send_error(s, m, "Module not loaded");
 		return 0;
 	}
-	snprintf(cut, (sizeof(filename) - strlen(filename)) - 1, ".c");
-	ast_debug(1, "**** ModuleCheck .c file %s\n", filename);
-#if !defined(LOW_MEMORY)
-	version = ast_file_version_find(filename);
-#endif
 
 	if (!ast_strlen_zero(id)) {
 		snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
@@ -5970,7 +5993,7 @@ static int manager_modulecheck(struct mansession *s, const struct message *m)
 	}
 	astman_append(s, "Response: Success\r\n%s", idText);
 #if !defined(LOW_MEMORY)
-	astman_append(s, "Version: %s\r\n\r\n", version ? version : "");
+	astman_append(s, "Version: %s\r\n\r\n", ast_get_version());
 #endif
 	return 0;
 }
@@ -6003,14 +6026,31 @@ static int manager_moduleload(struct mansession *s, const struct message *m)
 			astman_send_ack(s, m, "Module unloaded.");
 		}
 	} else if (!strcasecmp(loadtype, "reload")) {
+		/* TODO: Unify the ack/error messages here with action_reload */
 		if (!ast_strlen_zero(module)) {
-			res = ast_module_reload(module);
-			if (res == 0) {
+			enum ast_module_reload_result reload_res = ast_module_reload(module);
+
+			switch (reload_res) {
+			case AST_MODULE_RELOAD_NOT_FOUND:
 				astman_send_error(s, m, "No such module.");
-			} else if (res == 1) {
+				break;
+			case AST_MODULE_RELOAD_NOT_IMPLEMENTED:
 				astman_send_error(s, m, "Module does not support reload action.");
-			} else {
+				break;
+			case AST_MODULE_RELOAD_ERROR:
+				astman_send_error(s, m, "An unknown error occurred");
+				break;
+			case AST_MODULE_RELOAD_IN_PROGRESS:
+				astman_send_error(s, m, "A reload is in progress");
+				break;
+			case AST_MODULE_RELOAD_UNINITIALIZED:
+				astman_send_error(s, m, "Module not initialized");
+				break;
+			case AST_MODULE_RELOAD_QUEUED:
+			case AST_MODULE_RELOAD_SUCCESS:
+				/* Treat a queued request as success */
 				astman_send_ack(s, m, "Module reloaded.");
+				break;
 			}
 		} else {
 			ast_module_reload(NULL);	/* Reload all modules */
@@ -6723,9 +6763,9 @@ static int ast_manager_register_struct(struct manager_action *act)
 			return -1;
 		}
 		if (ret > 0) { /* Insert these alphabetically */
-			prev = cur;
 			break;
 		}
+		prev = cur;
 	}
 
 	ao2_t_ref(act, +1, "action object added to list");
@@ -8563,7 +8603,7 @@ static int __init_manager(int reload, int by_external_config)
 #endif
 		int res;
 
-		ast_register_atexit(manager_shutdown);
+		ast_register_cleanup(manager_shutdown);
 
 		res = STASIS_MESSAGE_TYPE_INIT(ast_manager_get_generic_type);
 		if (res != 0) {
diff --git a/main/manager_bridges.c b/main/manager_bridges.c
index 29794b5..e190baa 100644
--- a/main/manager_bridges.c
+++ b/main/manager_bridges.c
@@ -25,7 +25,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419342 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/stasis_bridges.h"
 #include "asterisk/stasis_channels.h"
@@ -94,7 +94,7 @@ static struct stasis_message_router *bridge_state_router;
 		<syntax>
 			<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
 			<parameter name="BridgeUniqueid" required="true">
-				<para>The unique ID of the bridge about which to retreive information.</para>
+				<para>The unique ID of the bridge about which to retrieve information.</para>
 			</parameter>
 		</syntax>
 		<description>
@@ -364,12 +364,17 @@ static int filter_bridge_type_cb(void *obj, void *arg, int flags)
 	return strcmp(bridge_type, snapshot->technology) ? CMP_MATCH : 0;
 }
 
+struct bridge_list_data {
+	const char *id_text;
+	int count;
+};
+
 static int send_bridge_list_item_cb(void *obj, void *arg, void *data, int flags)
 {
 	struct ast_bridge_snapshot *snapshot = stasis_message_data(obj);
 	struct mansession *s = arg;
-	char *id_text = data;
-	RAII_VAR(struct ast_str *, bridge_info, ast_manager_build_bridge_state_string(snapshot), ast_free_ptr);
+	struct bridge_list_data *list_data = data;
+	RAII_VAR(struct ast_str *, bridge_info, ast_manager_build_bridge_state_string(snapshot), ast_free);
 
 	if (!bridge_info) {
 		return 0;
@@ -380,8 +385,9 @@ static int send_bridge_list_item_cb(void *obj, void *arg, void *data, int flags)
 		"%s"
 		"%s"
 		"\r\n",
-		ast_str_buffer(bridge_info),
-		id_text);
+		list_data->id_text,
+		ast_str_buffer(bridge_info));
+	++list_data->count;
 	return 0;
 }
 
@@ -391,6 +397,7 @@ static int manager_bridges_list(struct mansession *s, const struct message *m)
 	const char *type_filter = astman_get_header(m, "BridgeType");
 	RAII_VAR(struct ast_str *, id_text, ast_str_create(128), ast_free);
 	RAII_VAR(struct ao2_container *, bridges, NULL, ao2_cleanup);
+	struct bridge_list_data list_data;
 
 	if (!id_text) {
 		astman_send_error(s, m, "Internal error");
@@ -407,20 +414,21 @@ static int manager_bridges_list(struct mansession *s, const struct message *m)
 		return -1;
 	}
 
-	astman_send_ack(s, m, "Bridge listing will follow");
+	astman_send_listack(s, m, "Bridge listing will follow", "start");
 
 	if (!ast_strlen_zero(type_filter)) {
 		char *type_filter_dup = ast_strdupa(type_filter);
-		ao2_callback(bridges, OBJ_MULTIPLE | OBJ_NODATA | OBJ_UNLINK, filter_bridge_type_cb, type_filter_dup);
+
+		ao2_callback(bridges, OBJ_MULTIPLE | OBJ_NODATA | OBJ_UNLINK,
+			filter_bridge_type_cb, type_filter_dup);
 	}
 
-	ao2_callback_data(bridges, OBJ_NODATA, send_bridge_list_item_cb, s, ast_str_buffer(id_text));
+	list_data.id_text = ast_str_buffer(id_text);
+	list_data.count = 0;
+	ao2_callback_data(bridges, OBJ_NODATA, send_bridge_list_item_cb, s, &list_data);
 
-	astman_append(s,
-		"Event: BridgeListComplete\r\n"
-		"%s"
-		"\r\n",
-		ast_str_buffer(id_text));
+	astman_send_list_complete_start(s, m, "BridgeListComplete", list_data.count);
+	astman_send_list_complete_end(s);
 
 	return 0;
 }
@@ -429,13 +437,13 @@ static int send_bridge_info_item_cb(void *obj, void *arg, void *data, int flags)
 {
 	char *uniqueid = obj;
 	struct mansession *s = arg;
-	char *id_text = data;
+	struct bridge_list_data *list_data = data;
 	RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
 	struct ast_channel_snapshot *snapshot;
 	RAII_VAR(struct ast_str *, channel_text, NULL, ast_free);
+
 	msg = stasis_cache_get(ast_channel_cache(),
 		ast_channel_snapshot_type(), uniqueid);
-
 	if (!msg) {
 		return 0;
 	}
@@ -455,8 +463,9 @@ static int send_bridge_info_item_cb(void *obj, void *arg, void *data, int flags)
 		"%s"
 		"%s"
 		"\r\n",
-		ast_str_buffer(channel_text),
-		id_text);
+		list_data->id_text,
+		ast_str_buffer(channel_text));
+	++list_data->count;
 	return 0;
 }
 
@@ -468,6 +477,7 @@ static int manager_bridge_info(struct mansession *s, const struct message *m)
 	RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
 	RAII_VAR(struct ast_str *, bridge_info, NULL, ast_free);
 	struct ast_bridge_snapshot *snapshot;
+	struct bridge_list_data list_data;
 
 	if (!id_text) {
 		astman_send_error(s, m, "Internal error");
@@ -489,20 +499,24 @@ static int manager_bridge_info(struct mansession *s, const struct message *m)
 		return 0;
 	}
 
-	astman_send_ack(s, m, "Bridge channel listing will follow");
-
 	snapshot = stasis_message_data(msg);
 	bridge_info = ast_manager_build_bridge_state_string(snapshot);
+	if (!bridge_info) {
+		astman_send_error(s, m, "Internal error");
+		return -1;
+	}
 
-	ao2_callback_data(snapshot->channels, OBJ_NODATA, send_bridge_info_item_cb, s, ast_str_buffer(id_text));
+	astman_send_listack(s, m, "Bridge channel listing will follow", "start");
 
-	astman_append(s,
-		"Event: BridgeInfoComplete\r\n"
-		"%s"
-		"%s"
-		"\r\n",
-		S_COR(bridge_info, ast_str_buffer(bridge_info), ""),
-		ast_str_buffer(id_text));
+	list_data.id_text = ast_str_buffer(id_text);
+	list_data.count = 0;
+	ao2_callback_data(snapshot->channels, OBJ_NODATA, send_bridge_info_item_cb, s, &list_data);
+
+	astman_send_list_complete_start(s, m, "BridgeInfoComplete", list_data.count);
+	if (!ast_strlen_zero(ast_str_buffer(bridge_info))) {
+		astman_append(s, "%s", ast_str_buffer(bridge_info));
+	}
+	astman_send_list_complete_end(s);
 
 	return 0;
 }
@@ -577,10 +591,7 @@ static void manager_bridging_cleanup(void)
 {
 	stasis_forward_cancel(topic_forwarder);
 	topic_forwarder = NULL;
-}
 
-static void manager_bridging_shutdown(void)
-{
 	ast_manager_unregister("BridgeList");
 	ast_manager_unregister("BridgeInfo");
 	ast_manager_unregister("BridgeDestroy");
@@ -598,7 +609,6 @@ int manager_bridging_init(void)
 		return 0;
 	}
 
-	ast_register_atexit(manager_bridging_shutdown);
 	ast_register_cleanup(manager_bridging_cleanup);
 
 	manager_topic = ast_manager_get_topic();
@@ -642,7 +652,7 @@ int manager_bridging_init(void)
 	 * thing and fail it.
 	 */
 	if (ret) {
-		manager_bridging_shutdown();
+		manager_bridging_cleanup();
 		return -1;
 	}
 
diff --git a/main/manager_channels.c b/main/manager_channels.c
index e3c213a..adef639 100644
--- a/main/manager_channels.c
+++ b/main/manager_channels.c
@@ -28,7 +28,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 429064 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/callerid.h"
 #include "asterisk/channel.h"
@@ -408,6 +408,7 @@ struct ast_str *ast_manager_build_channel_state_string_prefix(
 {
 	struct ast_str *out = ast_str_create(1024);
 	int res = 0;
+	char *caller_name, *connected_name;
 
 	if (!out) {
 		return NULL;
@@ -418,6 +419,9 @@ struct ast_str *ast_manager_build_channel_state_string_prefix(
 		return NULL;
 	}
 
+	caller_name = ast_escape_c_alloc(snapshot->caller_name);
+	connected_name = ast_escape_c_alloc(snapshot->connected_name);
+
 	res = ast_str_set(&out, 0,
 		"%sChannel: %s\r\n"
 		"%sChannelState: %u\r\n"
@@ -426,38 +430,50 @@ struct ast_str *ast_manager_build_channel_state_string_prefix(
 		"%sCallerIDName: %s\r\n"
 		"%sConnectedLineNum: %s\r\n"
 		"%sConnectedLineName: %s\r\n"
+		"%sLanguage: %s\r\n"
 		"%sAccountCode: %s\r\n"
 		"%sContext: %s\r\n"
 		"%sExten: %s\r\n"
 		"%sPriority: %d\r\n"
-		"%sUniqueid: %s\r\n",
+		"%sUniqueid: %s\r\n"
+		"%sLinkedid: %s\r\n",
 		prefix, snapshot->name,
 		prefix, snapshot->state,
 		prefix, ast_state2str(snapshot->state),
 		prefix, S_OR(snapshot->caller_number, "<unknown>"),
-		prefix, S_OR(snapshot->caller_name, "<unknown>"),
+		prefix, S_OR(caller_name, "<unknown>"),
 		prefix, S_OR(snapshot->connected_number, "<unknown>"),
-		prefix, S_OR(snapshot->connected_name, "<unknown>"),
+		prefix, S_OR(connected_name, "<unknown>"),
+		prefix, snapshot->language,
 		prefix, snapshot->accountcode,
 		prefix, snapshot->context,
 		prefix, snapshot->exten,
 		prefix, snapshot->priority,
-		prefix, snapshot->uniqueid);
+		prefix, snapshot->uniqueid,
+		prefix, snapshot->linkedid);
 
 	if (!res) {
 		ast_free(out);
+		ast_free(caller_name);
+		ast_free(connected_name);
 		return NULL;
 	}
 
 	if (snapshot->manager_vars) {
 		struct ast_var_t *var;
+		char *val;
 		AST_LIST_TRAVERSE(snapshot->manager_vars, var, entries) {
+			val = ast_escape_c_alloc(var->value);
 			ast_str_append(&out, 0, "%sChanVariable: %s=%s\r\n",
 				       prefix,
-				       var->name, var->value);
+				       var->name, S_OR(val, ""));
+			ast_free(val);
 		}
 	}
 
+	ast_free(caller_name);
+	ast_free(connected_name);
+
 	return out;
 }
 
@@ -554,6 +570,9 @@ static struct ast_manager_event_blob *channel_new_callerid(
 	struct ast_channel_snapshot *old_snapshot,
 	struct ast_channel_snapshot *new_snapshot)
 {
+	struct ast_manager_event_blob *res;
+	char *callerid;
+
 	/* No NewCallerid event on cache clear or first event */
 	if (!old_snapshot || !new_snapshot) {
 		return NULL;
@@ -563,11 +582,19 @@ static struct ast_manager_event_blob *channel_new_callerid(
 		return NULL;
 	}
 
-	return ast_manager_event_blob_create(
+	if (!(callerid = ast_escape_c_alloc(
+		      ast_describe_caller_presentation(new_snapshot->caller_pres)))) {
+		return NULL;
+	}
+
+	res = ast_manager_event_blob_create(
 		EVENT_FLAG_CALL, "NewCallerid",
 		"CID-CallingPres: %d (%s)\r\n",
 		new_snapshot->caller_pres,
-		ast_describe_caller_presentation(new_snapshot->caller_pres));
+		callerid);
+
+	ast_free(callerid);
+	return res;
 }
 
 static struct ast_manager_event_blob *channel_new_connected_line(
@@ -1059,20 +1086,22 @@ static void channel_hold_cb(void *data, struct stasis_subscription *sub,
 	struct stasis_message *message)
 {
 	struct ast_channel_blob *obj = stasis_message_data(message);
-	const char *musicclass;
-	RAII_VAR(struct ast_str *, musicclass_string, NULL, ast_free);
-	RAII_VAR(struct ast_str *, channel_event_string, NULL, ast_free);
+	struct ast_str *musicclass_string = ast_str_create(32);
+	struct ast_str *channel_event_string;
 
-	if (!(musicclass_string = ast_str_create(32))) {
+	if (!musicclass_string) {
 		return;
 	}
 
 	channel_event_string = ast_manager_build_channel_state_string(obj->snapshot);
 	if (!channel_event_string) {
+		ast_free(musicclass_string);
 		return;
 	}
 
 	if (obj->blob) {
+		const char *musicclass;
+
 		musicclass = ast_json_string_get(ast_json_object_get(obj->blob, "musicclass"));
 
 		if (!ast_strlen_zero(musicclass)) {
@@ -1085,13 +1114,16 @@ static void channel_hold_cb(void *data, struct stasis_subscription *sub,
 		"%s",
 		ast_str_buffer(channel_event_string),
 		ast_str_buffer(musicclass_string));
+
+	ast_free(musicclass_string);
+	ast_free(channel_event_string);
 }
 
 static void channel_unhold_cb(void *data, struct stasis_subscription *sub,
 	struct stasis_message *message)
 {
 	struct ast_channel_blob *obj = stasis_message_data(message);
-	RAII_VAR(struct ast_str *, channel_event_string, NULL, ast_free);
+	struct ast_str *channel_event_string;
 
 	channel_event_string = ast_manager_build_channel_state_string(obj->snapshot);
 	if (!channel_event_string) {
@@ -1101,6 +1133,8 @@ static void channel_unhold_cb(void *data, struct stasis_subscription *sub,
 	manager_event(EVENT_FLAG_CALL, "Unhold",
 		"%s",
 		ast_str_buffer(channel_event_string));
+
+	ast_free(channel_event_string);
 }
 
 static void manager_channels_shutdown(void)
@@ -1134,7 +1168,7 @@ int manager_channels_init(void)
 		return -1;
 	}
 
-	ast_register_atexit(manager_channels_shutdown);
+	ast_register_cleanup(manager_channels_shutdown);
 
 	ret |= stasis_message_router_add_cache_update(message_router,
 		ast_channel_snapshot_type(), channel_snapshot_update, NULL);
diff --git a/main/manager_endpoints.c b/main/manager_endpoints.c
index c9cce2a..b2a4573 100644
--- a/main/manager_endpoints.c
+++ b/main/manager_endpoints.c
@@ -28,7 +28,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 400186 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/callerid.h"
 #include "asterisk/channel.h"
@@ -61,7 +61,7 @@ int manager_endpoints_init(void)
 		return 0;
 	}
 
-	ast_register_atexit(manager_endpoints_shutdown);
+	ast_register_cleanup(manager_endpoints_shutdown);
 
 	endpoint_topic = ast_endpoint_topic_all_cached();
 	if (!endpoint_topic) {
@@ -75,6 +75,7 @@ int manager_endpoints_init(void)
 	}
 
 	ret |= stasis_message_router_add(endpoint_router, ast_endpoint_state_type(), endpoint_state_cb, NULL);
+	ret |= stasis_message_router_add(endpoint_router, ast_endpoint_contact_state_type(), endpoint_state_cb, NULL);
 
 	/* If somehow we failed to add any routes, just shut down the whole
 	 * thing and fail it.
diff --git a/main/manager_mwi.c b/main/manager_mwi.c
index a1dff74..8facc90 100644
--- a/main/manager_mwi.c
+++ b/main/manager_mwi.c
@@ -25,7 +25,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 400186 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/manager.h"
 #include "asterisk/app.h"
@@ -176,7 +176,7 @@ int manager_mwi_init(void)
 		return -1;
 	}
 
-	ast_register_atexit(manager_mwi_shutdown);
+	ast_register_cleanup(manager_mwi_shutdown);
 
 	ret |= stasis_message_router_add(message_router,
 					 ast_mwi_state_type(),
diff --git a/main/manager_system.c b/main/manager_system.c
index 1ab7b8b..b852c52 100644
--- a/main/manager_system.c
+++ b/main/manager_system.c
@@ -25,7 +25,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 400186 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/stasis.h"
 #include "asterisk/stasis_message_router.h"
@@ -67,7 +67,7 @@ int manager_system_init(void)
 		return -1;
 	}
 
-	ast_register_atexit(manager_system_shutdown);
+	ast_register_cleanup(manager_system_shutdown);
 
 	/* If somehow we failed to add any routes, just shut down the whole
 	 * thing and fail it.
diff --git a/main/max_forwards.c b/main/max_forwards.c
new file mode 100644
index 0000000..8f1d4ee
--- /dev/null
+++ b/main/max_forwards.c
@@ -0,0 +1,165 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2015, Digium, Inc.
+ *
+ * Mark Michelson <mmichelson at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not mfrectly 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, mfstributed 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 "asterisk/max_forwards.h"
+#include "asterisk/channel.h"
+
+#define DEFAULT_MAX_FORWARDS 20
+
+/*!
+ * \brief Channel datastore data for max forwards
+ */
+struct max_forwards {
+	/*! The starting count. Used to allow resetting to the original value */
+	int starting_count;
+	/*! The current count. When this reaches 0, you're outta luck */
+	int current_count;
+};
+
+static struct max_forwards *max_forwards_alloc(int starting_count, int current_count)
+{
+	struct max_forwards *mf;
+
+	mf = ast_malloc(sizeof(*mf));
+	if (!mf) {
+		return NULL;
+	}
+
+	mf->starting_count = starting_count;
+	mf->current_count = current_count;
+
+	return mf;
+}
+
+static void *max_forwards_duplicate(void *data)
+{
+	struct max_forwards *mf = data;
+
+	return max_forwards_alloc(mf->starting_count, mf->current_count);
+}
+
+static void max_forwards_destroy(void *data)
+{
+	ast_free(data);
+}
+
+const struct ast_datastore_info max_forwards_info = {
+	.type = "mfaled-interface",
+	.duplicate = max_forwards_duplicate,
+	.destroy = max_forwards_destroy,
+};
+
+static struct ast_datastore *max_forwards_datastore_alloc(struct ast_channel *chan,
+		int starting_count)
+{
+	struct ast_datastore *mf_datastore;
+	struct max_forwards *mf;
+
+	mf_datastore = ast_datastore_alloc(&max_forwards_info, NULL);
+	if (!mf_datastore) {
+		return NULL;
+	}
+	mf_datastore->inheritance = DATASTORE_INHERIT_FOREVER;
+
+	mf = max_forwards_alloc(starting_count, starting_count);
+	if (!mf) {
+		ast_datastore_free(mf_datastore);
+		return NULL;
+	}
+	mf_datastore->data = mf;
+
+	ast_channel_datastore_add(chan, mf_datastore);
+
+	return mf_datastore;
+}
+
+static struct ast_datastore *max_forwards_datastore_find_or_alloc(struct ast_channel *chan)
+{
+	struct ast_datastore *mf_datastore;
+
+	mf_datastore = ast_channel_datastore_find(chan, &max_forwards_info, NULL);
+	if (!mf_datastore) {
+		mf_datastore = max_forwards_datastore_alloc(chan, DEFAULT_MAX_FORWARDS);
+	}
+
+	return mf_datastore;
+}
+
+int ast_max_forwards_set(struct ast_channel *chan, int starting_count)
+{
+	struct ast_datastore *mf_datastore;
+	struct max_forwards *mf;
+
+	mf_datastore = max_forwards_datastore_find_or_alloc(chan);
+	if (!mf_datastore) {
+		return -1;
+	}
+
+	mf = mf_datastore->data;
+	mf->starting_count = mf->current_count = starting_count;
+
+	return 0;
+}
+
+int ast_max_forwards_get(struct ast_channel *chan)
+{
+	struct ast_datastore *mf_datastore;
+	struct max_forwards *mf;
+
+	mf_datastore = max_forwards_datastore_find_or_alloc(chan);
+	if (!mf_datastore) {
+		return -1;
+	}
+
+	mf = mf_datastore->data;
+	return mf->current_count;
+}
+
+int ast_max_forwards_decrement(struct ast_channel *chan)
+{
+	struct ast_datastore *mf_datastore;
+	struct max_forwards *mf;
+
+	mf_datastore = max_forwards_datastore_find_or_alloc(chan);
+	if (!mf_datastore) {
+		return -1;
+	}
+
+	mf = mf_datastore->data;
+	--mf->current_count;
+
+	return 0;
+}
+
+int ast_max_forwards_reset(struct ast_channel *chan)
+{
+	struct ast_datastore *mf_datastore;
+	struct max_forwards *mf;
+
+	mf_datastore = max_forwards_datastore_find_or_alloc(chan);
+	if (!mf_datastore) {
+		return -1;
+	}
+
+	mf = mf_datastore->data;
+	mf->current_count = mf->starting_count;
+
+	return 0;
+}
diff --git a/main/md5.c b/main/md5.c
index 9342931..5e79729 100644
--- a/main/md5.c
+++ b/main/md5.c
@@ -22,7 +22,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 364462 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/endian.h"
 #include "asterisk/md5.h"
diff --git a/main/message.c b/main/message.c
index b922fc7..01a1c9b 100644
--- a/main/message.c
+++ b/main/message.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 424692 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/_private.h"
 
@@ -743,6 +743,7 @@ static void chan_cleanup(struct ast_channel *chan)
 	struct ast_datastore *msg_ds, *ds;
 	struct varshead *headp;
 	struct ast_var_t *vardata;
+	struct ast_frame *cur;
 
 	ast_channel_lock(chan);
 
@@ -772,6 +773,13 @@ static void chan_cleanup(struct ast_channel *chan)
 	}
 
 	/*
+	 * Remove frames from read queue
+	 */
+	while ((cur = AST_LIST_REMOVE_HEAD(ast_channel_readq(chan), frame_list))) {
+		ast_frfree(cur);
+	}
+
+	/*
 	 * Restore msg datastore.
 	 */
 	if (msg_ds) {
@@ -1516,7 +1524,7 @@ int ast_msg_init(void)
 	res |= ast_register_application2(app_msg_send, msg_send_exec, NULL, NULL, NULL);
 	res |= ast_manager_register_xml_core("MessageSend", EVENT_FLAG_MESSAGE, action_messagesend);
 
-	ast_register_atexit(message_shutdown);
+	ast_register_cleanup(message_shutdown);
 
 	return res;
 }
diff --git a/main/mixmonitor.c b/main/mixmonitor.c
index 290f650..9a6d86c 100644
--- a/main/mixmonitor.c
+++ b/main/mixmonitor.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 395686 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/lock.h"
 #include "asterisk/logger.h"
diff --git a/main/named_acl.c b/main/named_acl.c
index 62aa06d..3b81c8c 100644
--- a/main/named_acl.c
+++ b/main/named_acl.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420124 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/config.h"
 #include "asterisk/config_options.h"
@@ -590,10 +590,7 @@ int ast_named_acl_init()
 	aco_option_register(&cfg_info, "permit", ACO_EXACT, named_acl_types, NULL, OPT_ACL_T, 1, FLDSET(struct named_acl, ha));
 	aco_option_register(&cfg_info, "deny", ACO_EXACT, named_acl_types, NULL, OPT_ACL_T, 0, FLDSET(struct named_acl, ha));
 
-	if (aco_process_config(&cfg_info, 0)) {
-		aco_info_destroy(&cfg_info);
-		return 0;
-	}
+	aco_process_config(&cfg_info, 0);
 
 	return 0;
 }
diff --git a/main/netsock.c b/main/netsock.c
index 4918989..c11f14a 100644
--- a/main/netsock.c
+++ b/main/netsock.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 417167 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #ifndef __linux__
 #if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__Darwin__) || defined(__GLIBC__)
diff --git a/main/netsock2.c b/main/netsock2.c
index 7519379..8d36805 100644
--- a/main/netsock2.c
+++ b/main/netsock2.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 416738 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/config.h"
 #include "asterisk/netsock2.h"
@@ -287,11 +287,13 @@ int ast_sockaddr_resolve(struct ast_sockaddr **addrs, const char *str,
 	int	e, i, res_cnt;
 
 	if (!str) {
+		*addrs = NULL;
 		return 0;
 	}
 
 	s = ast_strdupa(str);
 	if (!ast_sockaddr_split_hostport(s, &host, &port, flags)) {
+		*addrs = NULL;
 		return 0;
 	}
 
@@ -302,6 +304,7 @@ int ast_sockaddr_resolve(struct ast_sockaddr **addrs, const char *str,
 	if ((e = getaddrinfo(host, port, &hints, &res))) {
 		ast_log(LOG_ERROR, "getaddrinfo(\"%s\", \"%s\", ...): %s\n",
 			host, S_OR(port, "(null)"), gai_strerror(e));
+		*addrs = NULL;
 		return 0;
 	}
 
@@ -311,6 +314,7 @@ int ast_sockaddr_resolve(struct ast_sockaddr **addrs, const char *str,
 	}
 
 	if (res_cnt == 0) {
+		*addrs = NULL;
 		goto cleanup;
 	}
 
diff --git a/main/optional_api.c b/main/optional_api.c
index 5af3868..92f36ea 100644
--- a/main/optional_api.c
+++ b/main/optional_api.c
@@ -18,7 +18,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 414798 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/optional_api.h"
 #include "asterisk/utils.h"
diff --git a/main/parking.c b/main/parking.c
index 61b854d..f7f1dfb 100644
--- a/main/parking.c
+++ b/main/parking.c
@@ -25,7 +25,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 414403 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/_private.h"
 #include "asterisk/astobj2.h"
diff --git a/main/pbx.c b/main/pbx.c
index d7d1431..79192d0 100644
--- a/main/pbx.c
+++ b/main/pbx.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 427711 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/_private.h"
 #include "asterisk/paths.h"	/* use ast_config_AST_SYSTEM_NAME */
@@ -72,6 +72,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 427711 $")
 #include "asterisk/astobj2.h"
 #include "asterisk/stasis_channels.h"
 #include "asterisk/dial.h"
+#include "asterisk/vector.h"
 
 /*!
  * \note I M P O R T A N T :
@@ -1046,8 +1047,11 @@ struct ast_hint {
 
 	char context_name[AST_MAX_CONTEXT];/*!< Context of destroyed hint extension. */
 	char exten_name[AST_MAX_EXTENSION];/*!< Extension of destroyed hint extension. */
+
+	AST_VECTOR(, char *) devices; /*!< Devices associated with the hint */
 };
 
+STASIS_MESSAGE_TYPE_DEFN_LOCAL(hint_change_message_type);
 
 #define HINTDEVICE_DATA_LENGTH 16
 AST_THREADSTORAGE(hintdevice_data);
@@ -1077,15 +1081,28 @@ struct ast_hintdevice {
 	char hintdevice[1];
 };
 
-
 /*!
  * \note Using the device for hash
  */
 static int hintdevice_hash_cb(const void *obj, const int flags)
 {
-	const struct ast_hintdevice *ext = obj;
+	const struct ast_hintdevice *ext;
+	const char *key;
+
+	switch (flags & OBJ_SEARCH_MASK) {
+	case OBJ_SEARCH_KEY:
+		key = obj;
+		break;
+	case OBJ_SEARCH_OBJECT:
+		ext = obj;
+		key = ext->hintdevice;
+		break;
+	default:
+		ast_assert(0);
+		return 0;
+	}
 
-	return ast_str_case_hash(ext->hintdevice);
+	return ast_str_case_hash(key);
 }
 /*!
  * \note Devices on hints are not unique so no CMP_STOP is returned
@@ -1094,29 +1111,58 @@ static int hintdevice_hash_cb(const void *obj, const int flags)
  */
 static int hintdevice_cmp_multiple(void *obj, void *arg, int flags)
 {
-	struct ast_hintdevice *ext = obj, *ext2 = arg;
+	struct ast_hintdevice *left = obj;
+	struct ast_hintdevice *right = arg;
+	const char *right_key = arg;
+	int cmp;
 
-	return !strcasecmp(ext->hintdevice, ext2->hintdevice) ? CMP_MATCH  : 0;
+	switch (flags & OBJ_SEARCH_MASK) {
+	case OBJ_SEARCH_OBJECT:
+		right_key = right->hintdevice;
+		/* Fall through */
+	case OBJ_SEARCH_KEY:
+		cmp = strcasecmp(left->hintdevice, right_key);
+		break;
+	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.
+		*/
+		cmp = strncmp(left->hintdevice, right_key, strlen(right_key));
+		break;
+	default:
+		ast_assert(0);
+		cmp = 0;
+		break;
+	}
+	return cmp ? 0 : CMP_MATCH;
 }
 
-/*
- * \details This is used with ao2_callback to remove old devices
- * when they are linked to the hint
-*/
-static int hintdevice_remove_cb(void *deviceobj, void *arg, int flags)
+/*! \internal \brief \c ao2_callback function to remove hintdevices */
+static int hintdevice_remove_cb(void *obj, void *arg, void *data, int flags)
 {
-	struct ast_hintdevice *device = deviceobj;
-	struct ast_hint *hint = arg;
+	struct ast_hintdevice *candidate = obj;
+	char *device = arg;
+	struct ast_hint *hint = data;
 
-	return (device->hint == hint) ? CMP_MATCH : 0;
+	if (!strcasecmp(candidate->hintdevice, device)
+		&& candidate->hint == hint) {
+		return CMP_MATCH;
+	}
+	return 0;
 }
 
 static int remove_hintdevice(struct ast_hint *hint)
 {
-	/* iterate through all devices and remove the devices which are linked to this hint */
-	ao2_t_callback(hintdevices, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK,
-		hintdevice_remove_cb, hint,
-		"callback to remove all devices which are linked to a hint");
+	while (AST_VECTOR_SIZE(&hint->devices) > 0) {
+		char *device = AST_VECTOR_GET(&hint->devices, 0);
+
+		ao2_t_callback_data(hintdevices, OBJ_SEARCH_KEY | OBJ_UNLINK | OBJ_NODATA,
+			hintdevice_remove_cb, device, hint, "Remove device from container");
+		AST_VECTOR_REMOVE_UNORDERED(&hint->devices, 0);
+		ast_free(device);
+	}
+
 	return 0;
 }
 
@@ -1159,16 +1205,34 @@ static int add_hintdevice(struct ast_hint *hint, const char *devicelist)
 	ast_str_set(&str, 0, "%s", devicelist);
 	parse = parse_hint_device(str);
 
-	while ((cur = strsep(&parse, "&"))) {
+	/* Spit on '&' and ',' to handle presence hints as well */
+	while ((cur = strsep(&parse, "&,"))) {
+		char *device_name;
+
 		devicelength = strlen(cur);
+		if (!devicelength) {
+			continue;
+		}
+
+		device_name = ast_strdup(cur);
+		if (!device_name) {
+			return -1;
+		}
+
 		device = ao2_t_alloc(sizeof(*device) + devicelength, hintdevice_destroy,
 			"allocating a hintdevice structure");
 		if (!device) {
+			ast_free(device_name);
 			return -1;
 		}
 		strcpy(device->hintdevice, cur);
 		ao2_ref(hint, +1);
 		device->hint = hint;
+		if (AST_VECTOR_APPEND(&hint->devices, device_name)) {
+			ast_free(device_name);
+			ao2_ref(device, -1);
+			return -1;
+		}
 		ao2_t_link(hintdevices, device, "Linking device into hintdevice container.");
 		ao2_t_ref(device, -1, "hintdevice is linked so we can unref");
 	}
@@ -5344,17 +5408,256 @@ static void get_device_state_causing_channels(struct ao2_container *c)
 	ao2_iterator_destroy(&iter);
 }
 
+static void device_state_notify_callbacks(struct ast_hint *hint, struct ast_str **hint_app)
+{
+	struct ao2_iterator cb_iter;
+	struct ast_state_cb *state_cb;
+	int state, same_state;
+	struct ao2_container *device_state_info;
+	int first_extended_cb_call = 1;
+	char context_name[AST_MAX_CONTEXT];
+	char exten_name[AST_MAX_EXTENSION];
+
+	ao2_lock(hint);
+	if (!hint->exten) {
+		/* The extension has already been destroyed */
+		ao2_unlock(hint);
+		return;
+	}
+
+	/*
+	 * Save off strings in case the hint extension gets destroyed
+	 * while we are notifying the watchers.
+	 */
+	ast_copy_string(context_name,
+			ast_get_context_name(ast_get_extension_context(hint->exten)),
+			sizeof(context_name));
+	ast_copy_string(exten_name, ast_get_extension_name(hint->exten),
+			sizeof(exten_name));
+	ast_str_set(hint_app, 0, "%s", ast_get_extension_app(hint->exten));
+	ao2_unlock(hint);
+
+	/*
+	 * Get device state for this hint.
+	 *
+	 * NOTE: We cannot hold any locks while determining the hint
+	 * device state or notifying the watchers without causing a
+	 * deadlock.  (conlock, hints, and hint)
+	 */
+
+	/* Make a container so state3 can fill it if we wish.
+	 * If that failed we simply do not provide the extended state info.
+	 */
+	device_state_info = alloc_device_state_info();
+
+	state = ast_extension_state3(*hint_app, device_state_info);
+	if ((same_state = state == hint->laststate) && (~state & AST_EXTENSION_RINGING)) {
+		ao2_cleanup(device_state_info);
+		return;
+	}
+
+	/* Device state changed since last check - notify the watchers. */
+	hint->laststate = state;	/* record we saw the change */
+
+	/* For general callbacks */
+	cb_iter = ao2_iterator_init(statecbs, 0);
+	for (; !same_state && (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
+		execute_state_callback(state_cb->change_cb,
+				       context_name,
+				       exten_name,
+				       state_cb->data,
+				       AST_HINT_UPDATE_DEVICE,
+				       hint,
+				       NULL);
+	}
+	ao2_iterator_destroy(&cb_iter);
+
+	/* For extension callbacks */
+	/* extended callbacks are called when the state changed or when AST_STATE_RINGING is
+	 * included. Normal callbacks are only called when the state changed.
+	 */
+	cb_iter = ao2_iterator_init(hint->callbacks, 0);
+	for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
+		if (state_cb->extended && first_extended_cb_call) {
+			/* Fill detailed device_state_info now that we know it is used by extd. callback */
+			first_extended_cb_call = 0;
+			get_device_state_causing_channels(device_state_info);
+		}
+		if (state_cb->extended || !same_state) {
+			execute_state_callback(state_cb->change_cb,
+					       context_name,
+					       exten_name,
+					       state_cb->data,
+					       AST_HINT_UPDATE_DEVICE,
+					       hint,
+					       state_cb->extended ? device_state_info : NULL);
+		}
+	}
+	ao2_iterator_destroy(&cb_iter);
+
+	ao2_cleanup(device_state_info);
+}
+
+static void presence_state_notify_callbacks(
+	struct stasis_message *msg, struct ast_hint *hint, struct ast_str **hint_app,
+	struct ast_presence_state_message *presence_state)
+{
+	struct ao2_iterator cb_iter;
+	struct ast_state_cb *state_cb;
+	char context_name[AST_MAX_CONTEXT];
+	char exten_name[AST_MAX_EXTENSION];
+
+	ao2_lock(hint);
+
+	if (!hint->exten) {
+		/* The extension has already been destroyed */
+		ao2_unlock(hint);
+		return;
+	}
+
+	if (hint_change_message_type() != stasis_message_type(msg)) {
+		const char *app;
+		char *parse;
+
+		/* Does this hint monitor the device that changed state? */
+		app = ast_get_extension_app(hint->exten);
+		if (ast_strlen_zero(app)) {
+			/* The hint does not monitor presence at all. */
+			ao2_unlock(hint);
+			return;
+		}
+
+		ast_str_set(hint_app, 0, "%s", app);
+		parse = parse_hint_presence(*hint_app);
+		if (ast_strlen_zero(parse)) {
+			ao2_unlock(hint);
+			return;
+		}
+		if (strcasecmp(parse, presence_state->provider)) {
+			/* The hint does not monitor the presence provider. */
+			ao2_unlock(hint);
+			return;
+		}
+	}
+
+	/*
+	 * Save off strings in case the hint extension gets destroyed
+	 * while we are notifying the watchers.
+	 */
+	ast_copy_string(context_name,
+			ast_get_context_name(ast_get_extension_context(hint->exten)),
+			sizeof(context_name));
+	ast_copy_string(exten_name, ast_get_extension_name(hint->exten),
+			sizeof(exten_name));
+	ast_str_set(hint_app, 0, "%s", ast_get_extension_app(hint->exten));
+
+	/* Check to see if update is necessary */
+	if ((hint->last_presence_state == presence_state->state) &&
+	    ((hint->last_presence_subtype && presence_state->subtype &&
+	      !strcmp(hint->last_presence_subtype, presence_state->subtype)) ||
+	     (!hint->last_presence_subtype && !presence_state->subtype)) &&
+	    ((hint->last_presence_message && presence_state->message &&
+	      !strcmp(hint->last_presence_message, presence_state->message)) ||
+	     (!hint->last_presence_message && !presence_state->message))) {
+		/* this update is the same as the last, do nothing */
+		ao2_unlock(hint);
+		return;
+	}
+
+	/* update new values */
+	ast_free(hint->last_presence_subtype);
+	ast_free(hint->last_presence_message);
+	hint->last_presence_state = presence_state->state;
+	hint->last_presence_subtype = presence_state->subtype ? ast_strdup(presence_state->subtype) : NULL;
+	hint->last_presence_message = presence_state->message ? ast_strdup(presence_state->message) : NULL;
+
+	/*
+	 * NOTE: We cannot hold any locks while notifying
+	 * the watchers without causing a deadlock.
+	 * (conlock, hints, and hint)
+	 */
+	ao2_unlock(hint);
+
+	/* For general callbacks */
+	cb_iter = ao2_iterator_init(statecbs, 0);
+	for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
+		execute_state_callback(state_cb->change_cb,
+				       context_name,
+				       exten_name,
+				       state_cb->data,
+				       AST_HINT_UPDATE_PRESENCE,
+				       hint,
+				       NULL);
+	}
+	ao2_iterator_destroy(&cb_iter);
+
+	/* For extension callbacks */
+	cb_iter = ao2_iterator_init(hint->callbacks, 0);
+	for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_cleanup(state_cb)) {
+		execute_state_callback(state_cb->change_cb,
+				       context_name,
+				       exten_name,
+				       state_cb->data,
+				       AST_HINT_UPDATE_PRESENCE,
+				       hint,
+				       NULL);
+	}
+	ao2_iterator_destroy(&cb_iter);
+}
+
+static int handle_hint_change_message_type(struct stasis_message *msg, enum ast_state_cb_update_reason reason)
+{
+	struct ast_hint *hint;
+	struct ast_str *hint_app;
+
+	if (hint_change_message_type() != stasis_message_type(msg)) {
+		return 0;
+	}
+
+	if (!(hint_app = ast_str_create(1024))) {
+		return -1;
+	}
+
+	hint = stasis_message_data(msg);
+
+	if (reason == AST_HINT_UPDATE_DEVICE) {
+		device_state_notify_callbacks(hint, &hint_app);
+	} else if (reason == AST_HINT_UPDATE_PRESENCE) {
+		char *presence_subtype = NULL;
+		char *presence_message = NULL;
+		int state;
+
+		state = extension_presence_state_helper(
+			hint->exten, &presence_subtype, &presence_message);
+
+		{
+			struct ast_presence_state_message presence_state = {
+				.state = state > 0 ? state : AST_PRESENCE_INVALID,
+				.subtype = presence_subtype,
+				.message = presence_message
+			};
+			presence_state_notify_callbacks(msg, hint, &hint_app, &presence_state);
+		}
+
+		ast_free(presence_subtype);
+		ast_free(presence_message);
+	}
+
+	ast_free(hint_app);
+	return 1;
+}
+
 static void device_state_cb(void *unused, struct stasis_subscription *sub, struct stasis_message *msg)
 {
 	struct ast_device_state_message *dev_state;
-	struct ast_hint *hint;
 	struct ast_str *hint_app;
 	struct ast_hintdevice *device;
 	struct ast_hintdevice *cmpdevice;
 	struct ao2_iterator *dev_iter;
-	struct ao2_iterator cb_iter;
-	char context_name[AST_MAX_CONTEXT];
-	char exten_name[AST_MAX_EXTENSION];
+
+	if (handle_hint_change_message_type(msg, AST_HINT_UPDATE_DEVICE)) {
+		return;
+	}
 
 	if (ast_device_state_message_type() != stasis_message_type(msg)) {
 		return;
@@ -5381,7 +5684,7 @@ static void device_state_cb(void *unused, struct stasis_subscription *sub, struc
 
 	ast_mutex_lock(&context_merge_lock);/* Hold off ast_merge_contexts_and_delete */
 	dev_iter = ao2_t_callback(hintdevices,
-		OBJ_POINTER | OBJ_MULTIPLE,
+		OBJ_SEARCH_OBJECT | OBJ_MULTIPLE,
 		hintdevice_cmp_multiple,
 		cmpdevice,
 		"find devices in container");
@@ -5392,94 +5695,9 @@ static void device_state_cb(void *unused, struct stasis_subscription *sub, struc
 	}
 
 	for (; (device = ao2_iterator_next(dev_iter)); ao2_t_ref(device, -1, "Next device")) {
-		struct ast_state_cb *state_cb;
-		int state;
-		int same_state;
-		struct ao2_container *device_state_info;
-		int first_extended_cb_call = 1;
-
-		if (!device->hint) {
-			/* Should never happen. */
-			continue;
+		if (device->hint) {
+			device_state_notify_callbacks(device->hint, &hint_app);
 		}
-		hint = device->hint;
-
-		ao2_lock(hint);
-		if (!hint->exten) {
-			/* The extension has already been destroyed */
-			ao2_unlock(hint);
-			continue;
-		}
-
-		/*
-		 * Save off strings in case the hint extension gets destroyed
-		 * while we are notifying the watchers.
-		 */
-		ast_copy_string(context_name,
-			ast_get_context_name(ast_get_extension_context(hint->exten)),
-			sizeof(context_name));
-		ast_copy_string(exten_name, ast_get_extension_name(hint->exten),
-			sizeof(exten_name));
-		ast_str_set(&hint_app, 0, "%s", ast_get_extension_app(hint->exten));
-		ao2_unlock(hint);
-
-		/*
-		 * Get device state for this hint.
-		 *
-		 * NOTE: We cannot hold any locks while determining the hint
-		 * device state or notifying the watchers without causing a
-		 * deadlock.  (conlock, hints, and hint)
-		 */
-		/* Make a container so state3 can fill it if we wish.
-		 * If that failed we simply do not provide the extended state info.
-		 */
-		device_state_info = alloc_device_state_info();
-		state = ast_extension_state3(hint_app, device_state_info);
-		if ((same_state = state == hint->laststate) && (~state & AST_EXTENSION_RINGING)) {
-			ao2_cleanup(device_state_info);
-			continue;
-		}
-
-		/* Device state changed since last check - notify the watchers. */
-		hint->laststate = state;	/* record we saw the change */
-
-		/* For general callbacks */
-		cb_iter = ao2_iterator_init(statecbs, 0);
-		for (; !same_state && (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
-			execute_state_callback(state_cb->change_cb,
-				context_name,
-				exten_name,
-				state_cb->data,
-				AST_HINT_UPDATE_DEVICE,
-				hint,
-				NULL);
-		}
-		ao2_iterator_destroy(&cb_iter);
-
-		/* For extension callbacks */
-		/* extended callbacks are called when the state changed or when AST_STATE_RINGING is
-		 * included. Normal callbacks are only called when the state changed.
-		 */
-		cb_iter = ao2_iterator_init(hint->callbacks, 0);
-		for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
-			if (state_cb->extended && first_extended_cb_call) {
-				/* Fill detailed device_state_info now that we know it is used by extd. callback */
-				first_extended_cb_call = 0;
-				get_device_state_causing_channels(device_state_info);
-			}
-			if (state_cb->extended || !same_state) {
-				execute_state_callback(state_cb->change_cb,
-					context_name,
-					exten_name,
-					state_cb->data,
-					AST_HINT_UPDATE_DEVICE,
-					hint,
-					state_cb->extended ? device_state_info : NULL);
-			}
-		}
-		ao2_iterator_destroy(&cb_iter);
-
-		ao2_cleanup(device_state_info);
 	}
 	ast_mutex_unlock(&context_merge_lock);
 
@@ -5693,6 +5911,7 @@ static int hint_id_cmp(void *obj, void *arg, int flags)
 static void destroy_hint(void *obj)
 {
 	struct ast_hint *hint = obj;
+	int i;
 
 	if (hint->callbacks) {
 		struct ast_state_cb *state_cb;
@@ -5722,6 +5941,12 @@ static void destroy_hint(void *obj)
 		}
 		ao2_ref(hint->callbacks, -1);
 	}
+
+	for (i = 0; i < AST_VECTOR_SIZE(&hint->devices); i++) {
+		char *device = AST_VECTOR_GET(&hint->devices, i);
+		ast_free(device);
+	}
+	AST_VECTOR_FREE(&hint->devices);
 	ast_free(hint->last_presence_subtype);
 	ast_free(hint->last_presence_message);
 }
@@ -5783,6 +6008,7 @@ static int ast_add_hint(struct ast_exten *e)
 	if (!hint_new) {
 		return -1;
 	}
+	AST_VECTOR_INIT(&hint_new->devices, 8);
 
 	/* Initialize new hint. */
 	hint_new->callbacks = ao2_container_alloc(1, NULL, hint_id_cmp);
@@ -5829,12 +6055,53 @@ static int ast_add_hint(struct ast_exten *e)
 			ast_get_context_name(ast_get_extension_context(e)));
 	}
 
+	/* if not dynamic */
+	if (!(strstr(e->app, "${") && e->exten[0] == '_')) {
+		struct ast_state_cb *state_cb;
+		struct ao2_iterator cb_iter;
+
+		/* For general callbacks */
+		cb_iter = ao2_iterator_init(statecbs, 0);
+		for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
+			execute_state_callback(state_cb->change_cb,
+					ast_get_context_name(ast_get_extension_context(e)),
+					ast_get_extension_name(e),
+					state_cb->data,
+					AST_HINT_UPDATE_DEVICE,
+					hint_new,
+					NULL);
+		}
+		ao2_iterator_destroy(&cb_iter);
+	}
 	ao2_unlock(hints);
 	ao2_ref(hint_new, -1);
 
 	return 0;
 }
 
+/*! \brief Publish a hint changed event  */
+static int publish_hint_change(struct ast_hint *hint, struct ast_exten *ne)
+{
+	struct stasis_message *message;
+
+	if (!hint_change_message_type()) {
+		return -1;
+	}
+
+	/* The message is going to be published to two topics so the hint needs two refs */
+	if (!(message = stasis_message_create(hint_change_message_type(), ao2_bump(hint)))) {
+		ao2_ref(hint, -1);
+		return -1;
+	}
+
+	stasis_publish(ast_device_state_topic_all(), message);
+	stasis_publish(ast_presence_state_topic_all(), message);
+
+	ao2_ref(message, -1);
+
+	return 0;
+}
+
 /*! \brief Change hint for an extension */
 static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
 {
@@ -5853,6 +6120,7 @@ static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
 	hint = ao2_find(hints, oe, OBJ_UNLINK);
 	if (!hint) {
 		ao2_unlock(hints);
+		ast_mutex_unlock(&context_merge_lock);
 		return -1;
 	}
 
@@ -5861,21 +6129,24 @@ static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
 	/* Update the hint and put it back in the hints container. */
 	ao2_lock(hint);
 	hint->exten = ne;
+
 	ao2_unlock(hint);
+
 	ao2_link(hints, hint);
 	if (add_hintdevice(hint, ast_get_extension_app(ne))) {
 		ast_log(LOG_WARNING, "Could not add devices for hint: %s@%s.\n",
 			ast_get_extension_name(ne),
 			ast_get_context_name(ast_get_extension_context(ne)));
 	}
-
 	ao2_unlock(hints);
+
+	publish_hint_change(hint, ne);
+
 	ao2_ref(hint, -1);
 
 	return 0;
 }
 
-
 /*! \brief Get hint for channel */
 int ast_get_hint(char *hint, int hintsize, char *name, int namesize, struct ast_channel *c, const char *context, const char *exten)
 {
@@ -7436,7 +7707,7 @@ static char *handle_show_hints(struct ast_cli_entry *e, int cmd, struct ast_cli_
 			continue;
 		}
 		watchers = ao2_container_count(hint->callbacks);
-		sprintf(buf, "%s@%s",
+		snprintf(buf, sizeof(buf), "%s@%s",
 			ast_get_extension_name(hint->exten),
 			ast_get_context_name(ast_get_extension_context(hint->exten)));
 
@@ -8329,14 +8600,13 @@ static int manager_show_dialplan(struct mansession *s, const struct message *m)
 		manager_dpsendack(s, m);
 	}
 
-	astman_append(s, "Event: ShowDialPlanComplete\r\n"
-		"EventList: Complete\r\n"
-		"ListItems: %d\r\n"
+	astman_send_list_complete_start(s, m, "ShowDialPlanComplete", counters.total_items);
+	astman_append(s,
 		"ListExtensions: %d\r\n"
 		"ListPriorities: %d\r\n"
-		"ListContexts: %d\r\n"
-		"%s"
-		"\r\n", counters.total_items, counters.total_exten, counters.total_prio, counters.total_context, idtext);
+		"ListContexts: %d\r\n",
+		counters.total_exten, counters.total_prio, counters.total_context);
+	astman_send_list_complete_end(s);
 
 	/* everything ok */
 	return 0;
@@ -8655,9 +8925,9 @@ struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts,
 		ast_rdlock_contexts();
 		local_contexts = &contexts;
 		tmp = ast_hashtab_lookup(contexts_table, &search);
-		ast_unlock_contexts();
 		if (tmp) {
 			tmp->refcount++;
+			ast_unlock_contexts();
 			return tmp;
 		}
 	} else { /* local contexts just in a linked list; search there for the new context; slow, linear search, but not frequent */
@@ -8681,11 +8951,13 @@ struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts,
 		tmp->refcount = 1;
 	} else {
 		ast_log(LOG_ERROR, "Danger! We failed to allocate a context for %s!\n", name);
+		if (!extcontexts) {
+			ast_unlock_contexts();
+		}
 		return NULL;
 	}
 
 	if (!extcontexts) {
-		ast_wrlock_contexts();
 		tmp->next = *local_contexts;
 		*local_contexts = tmp;
 		ast_hashtab_insert_safe(contexts_table, tmp); /*put this context into the tree */
@@ -8810,7 +9082,7 @@ static void context_merge(struct ast_context **extcontexts, struct ast_hashtab *
 				dupdstr = ast_strdup(prio_item->data);
 
 				res1 = ast_add_extension2(new, 0, prio_item->exten, prio_item->priority, prio_item->label,
-										  prio_item->matchcid ? prio_item->cidmatch : NULL, prio_item->app, dupdstr, prio_item->datad, prio_item->registrar);
+										  prio_item->matchcid ? prio_item->cidmatch : NULL, prio_item->app, dupdstr, ast_free_ptr, prio_item->registrar);
 				if (!res1 && new_exten_item && new_prio_item){
 					ast_verb(3,"Dropping old dialplan item %s/%s/%d [%s(%s)] (registrar=%s) due to conflict with new dialplan\n",
 							context->name, prio_item->exten, prio_item->priority, prio_item->app, (char*)prio_item->data, prio_item->registrar);
@@ -9584,18 +9856,24 @@ int ast_context_add_ignorepat2(struct ast_context *con, const char *value, const
 
 int ast_ignore_pattern(const char *context, const char *pattern)
 {
-	struct ast_context *con = ast_context_find(context);
+	int ret = 0;
+	struct ast_context *con;
 
+	ast_rdlock_contexts();
+	con = ast_context_find(context);
 	if (con) {
 		struct ast_ignorepat *pat;
 
 		for (pat = con->ignorepats; pat; pat = pat->next) {
-			if (ast_extension_match(pat->pattern, pattern))
-				return 1;
+			if (ast_extension_match(pat->pattern, pattern)) {
+				ret = 1;
+				break;
+			}
 		}
 	}
+	ast_unlock_contexts();
 
-	return 0;
+	return ret;
 }
 
 /*
@@ -10717,6 +10995,16 @@ void __ast_context_destroy(struct ast_context *list, struct ast_hashtab *context
 				exten_iter = ast_hashtab_start_traversal(tmp->root_table);
 				while ((exten_item=ast_hashtab_next(exten_iter))) {
 					int end_traversal = 1;
+
+					/*
+					 * If the extension could not be removed from the root_table due to
+					 * a loaded PBX app, it can exist here but have its peer_table be
+					 * destroyed due to a previous pass through this function.
+					 */
+					if (!exten_item->peer_table) {
+						continue;
+					}
+
 					prio_iter = ast_hashtab_start_traversal(exten_item->peer_table);
 					while ((prio_item=ast_hashtab_next(prio_iter))) {
 						char extension[AST_MAX_EXTENSION];
@@ -10793,6 +11081,22 @@ void __ast_context_destroy(struct ast_context *list, struct ast_hashtab *context
 	}
 }
 
+int ast_context_destroy_by_name(const char *context, const char *registrar)
+{
+	struct ast_context *con;
+	int ret = -1;
+
+	ast_wrlock_contexts();
+	con = ast_context_find(context);
+	if (con) {
+		ast_context_destroy(con, registrar);
+		ret = 0;
+	}
+	ast_unlock_contexts();
+
+	return ret;
+}
+
 void ast_context_destroy(struct ast_context *con, const char *registrar)
 {
 	ast_wrlock_contexts();
@@ -11470,6 +11774,8 @@ int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const
 	struct ast_var_t *newvariable;
 	struct varshead *headp;
 	const char *nametail = name;
+	/*! True if the old value was not an empty string. */
+	int old_value_existed = 0;
 
 	if (name[strlen(name) - 1] == ')') {
 		char *function = ast_strdupa(name);
@@ -11496,6 +11802,7 @@ int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const
 		if (strcmp(ast_var_name(newvariable), nametail) == 0) {
 			/* there is already such a variable, delete it */
 			AST_LIST_REMOVE_CURRENT(entries);
+			old_value_existed = !ast_strlen_zero(ast_var_value(newvariable));
 			ast_var_delete(newvariable);
 			break;
 		}
@@ -11508,6 +11815,9 @@ int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const
 		}
 		AST_LIST_INSERT_HEAD(headp, newvariable, entries);
 		ast_channel_publish_varset(chan, name, value);
+	} else if (old_value_existed) {
+		/* We just deleted a non-empty dialplan variable. */
+		ast_channel_publish_varset(chan, name, "");
 	}
 
 	if (chan)
@@ -11828,13 +12138,13 @@ static int pbx_builtin_sayphonetic(struct ast_channel *chan, const char *data)
 
 static void presence_state_cb(void *unused, struct stasis_subscription *sub, struct stasis_message *msg)
 {
-	struct ast_presence_state_message *presence_state = stasis_message_data(msg);
 	struct ast_hint *hint;
 	struct ast_str *hint_app = NULL;
 	struct ao2_iterator hint_iter;
-	struct ao2_iterator cb_iter;
-	char context_name[AST_MAX_CONTEXT];
-	char exten_name[AST_MAX_EXTENSION];
+
+	if (handle_hint_change_message_type(msg, AST_HINT_UPDATE_PRESENCE)) {
+		return;
+	}
 
 	if (stasis_message_type(msg) != ast_presence_state_message_type()) {
 		return;
@@ -11848,85 +12158,7 @@ static void presence_state_cb(void *unused, struct stasis_subscription *sub, str
 	ast_mutex_lock(&context_merge_lock);/* Hold off ast_merge_contexts_and_delete */
 	hint_iter = ao2_iterator_init(hints, 0);
 	for (; (hint = ao2_iterator_next(&hint_iter)); ao2_cleanup(hint)) {
-		struct ast_state_cb *state_cb;
-		const char *app;
-		char *parse;
-		SCOPED_AO2LOCK(lock, hint);
-
-		if (!hint->exten) {
-			/* The extension has already been destroyed */
-			continue;
-		}
-
-		/* Does this hint monitor the device that changed state? */
-		app = ast_get_extension_app(hint->exten);
-		if (ast_strlen_zero(app)) {
-			/* The hint does not monitor presence at all. */
-			continue;
-		}
-
-		ast_str_set(&hint_app, 0, "%s", app);
-		parse = parse_hint_presence(hint_app);
-		if (ast_strlen_zero(parse)) {
-			continue;
-		}
-		if (strcasecmp(parse, presence_state->provider)) {
-			/* The hint does not monitor the presence provider. */
-			continue;
-		}
-
-		/*
-		 * Save off strings in case the hint extension gets destroyed
-		 * while we are notifying the watchers.
-		 */
-		ast_copy_string(context_name,
-			ast_get_context_name(ast_get_extension_context(hint->exten)),
-			sizeof(context_name));
-		ast_copy_string(exten_name, ast_get_extension_name(hint->exten),
-			sizeof(exten_name));
-		ast_str_set(&hint_app, 0, "%s", ast_get_extension_app(hint->exten));
-
-		/* Check to see if update is necessary */
-		if ((hint->last_presence_state == presence_state->state) &&
-			((hint->last_presence_subtype && presence_state->subtype && !strcmp(hint->last_presence_subtype, presence_state->subtype)) || (!hint->last_presence_subtype && !presence_state->subtype)) &&
-			((hint->last_presence_message && presence_state->message && !strcmp(hint->last_presence_message, presence_state->message)) || (!hint->last_presence_message && !presence_state->message))) {
-
-			/* this update is the same as the last, do nothing */
-			continue;
-		}
-
-		/* update new values */
-		ast_free(hint->last_presence_subtype);
-		ast_free(hint->last_presence_message);
-		hint->last_presence_state = presence_state->state;
-		hint->last_presence_subtype = presence_state->subtype ? ast_strdup(presence_state->subtype) : NULL;
-		hint->last_presence_message = presence_state->message ? ast_strdup(presence_state->message) : NULL;
-
-		/* For general callbacks */
-		cb_iter = ao2_iterator_init(statecbs, 0);
-		for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
-			execute_state_callback(state_cb->change_cb,
-				context_name,
-				exten_name,
-				state_cb->data,
-				AST_HINT_UPDATE_PRESENCE,
-				hint,
-				NULL);
-		}
-		ao2_iterator_destroy(&cb_iter);
-
-		/* For extension callbacks */
-		cb_iter = ao2_iterator_init(hint->callbacks, 0);
-		for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_cleanup(state_cb)) {
-			execute_state_callback(state_cb->change_cb,
-				context_name,
-				exten_name,
-				state_cb->data,
-				AST_HINT_UPDATE_PRESENCE,
-				hint,
-				NULL);
-		}
-		ao2_iterator_destroy(&cb_iter);
+		presence_state_notify_callbacks(msg, hint, &hint_app, stasis_message_data(msg));
 	}
 	ao2_iterator_destroy(&hint_iter);
 	ast_mutex_unlock(&context_merge_lock);
@@ -12032,16 +12264,13 @@ static int action_extensionstatelist(struct mansession *s, const struct message
 		   ast_extension_state2str(hint->laststate));
 		ao2_unlock(hint);
 	}
-	astman_append(s, "Event: ExtensionStateListComplete\r\n");
-	if (!ast_strlen_zero(action_id)) {
-		astman_append(s, "ActionID: %s\r\n", action_id);
-	}
-	astman_append(s, "EventList: Complete\r\n"
-		"ListItems: %d\r\n\r\n", hint_count);
 
 	ao2_iterator_destroy(&it_hints);
 	ao2_unlock(hints);
 
+	astman_send_list_complete_start(s, m, "ExtensionStateListComplete", hint_count);
+	astman_send_list_complete_end(s);
+
 	return 0;
 }
 
@@ -12076,7 +12305,7 @@ int load_pbx(void)
 	int res = 0;
 	int x;
 
-	ast_register_atexit(unload_pbx);
+	ast_register_cleanup(unload_pbx);
 
 	/* Initialize the PBX */
 	ast_verb(1, "Asterisk PBX Core Initializing\n");
@@ -12489,15 +12718,20 @@ static int statecbs_cmp(void *obj, void *arg, int flags)
  */
 static void pbx_shutdown(void)
 {
+	STASIS_MESSAGE_TYPE_CLEANUP(hint_change_message_type);
+
 	if (hints) {
+		ao2_container_unregister("hints");
 		ao2_ref(hints, -1);
 		hints = NULL;
 	}
 	if (hintdevices) {
+		ao2_container_unregister("hintdevices");
 		ao2_ref(hintdevices, -1);
 		hintdevices = NULL;
 	}
 	if (statecbs) {
+		ao2_container_unregister("statecbs");
 		ao2_ref(statecbs, -1);
 		statecbs = NULL;
 	}
@@ -12507,13 +12741,59 @@ static void pbx_shutdown(void)
 	pbx_builtin_clear_globals();
 }
 
+static void print_hints_key(void *v_obj, void *where, ao2_prnt_fn *prnt)
+{
+	struct ast_hint *hint = v_obj;
+
+	if (!hint) {
+		return;
+	}
+	prnt(where, "%s@%s", ast_get_extension_name(hint->exten),
+		ast_get_context_name(ast_get_extension_context(hint->exten)));
+}
+
+static void print_hintdevices_key(void *v_obj, void *where, ao2_prnt_fn *prnt)
+{
+	struct ast_hintdevice *hintdevice = v_obj;
+
+	if (!hintdevice) {
+		return;
+	}
+	prnt(where, "%s => %s@%s", hintdevice->hintdevice,
+		ast_get_extension_name(hintdevice->hint->exten),
+		ast_get_context_name(ast_get_extension_context(hintdevice->hint->exten)));
+}
+
+static void print_statecbs_key(void *v_obj, void *where, ao2_prnt_fn *prnt)
+{
+	struct ast_state_cb *state_cb = v_obj;
+
+	if (!state_cb) {
+		return;
+	}
+	prnt(where, "%d", state_cb->id);
+}
+
 int ast_pbx_init(void)
 {
 	hints = ao2_container_alloc(HASH_EXTENHINT_SIZE, hint_hash, hint_cmp);
+	if (hints) {
+		ao2_container_register("hints", hints, print_hints_key);
+	}
 	hintdevices = ao2_container_alloc(HASH_EXTENHINT_SIZE, hintdevice_hash_cb, hintdevice_cmp_multiple);
+	if (hintdevices) {
+		ao2_container_register("hintdevices", hintdevices, print_hintdevices_key);
+	}
 	statecbs = ao2_container_alloc(1, NULL, statecbs_cmp);
+	if (statecbs) {
+		ao2_container_register("statecbs", statecbs, print_statecbs_key);
+	}
 
 	ast_register_cleanup(pbx_shutdown);
 
+	if (STASIS_MESSAGE_TYPE_INIT(hint_change_message_type) != 0) {
+		return -1;
+	}
+
 	return (hints && hintdevices && statecbs) ? 0 : -1;
 }
diff --git a/main/pickup.c b/main/pickup.c
index cf0e764..6f1e0f1 100644
--- a/main/pickup.c
+++ b/main/pickup.c
@@ -47,7 +47,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420124 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/pickup.h"
 #include "asterisk/channel.h"
@@ -403,7 +403,7 @@ static void pickup_shutdown(void)
 int ast_pickup_init(void)
 {
 	STASIS_MESSAGE_TYPE_INIT(ast_call_pickup_type);
-	ast_register_atexit(pickup_shutdown);
+	ast_register_cleanup(pickup_shutdown);
 
 	return 0;
 }
diff --git a/main/plc.c b/main/plc.c
index da8852d..1f02069 100644
--- a/main/plc.c
+++ b/main/plc.c
@@ -36,7 +36,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 369013 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <math.h>
 
diff --git a/main/poll.c b/main/poll.c
index 6cb13f9..e0f6955 100644
--- a/main/poll.c
+++ b/main/poll.c
@@ -1,5 +1,5 @@
 /*---------------------------------------------------------------------------*\
-  $Id: poll.c 285269 2010-09-07 19:09:08Z tilghman $
+  $Id$
 
   NAME
 
diff --git a/main/presencestate.c b/main/presencestate.c
index 294794f..207e2aa 100644
--- a/main/presencestate.c
+++ b/main/presencestate.c
@@ -57,7 +57,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420124 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/_private.h"
 #include "asterisk/utils.h"
@@ -290,7 +290,7 @@ static void do_presence_state_change(const char *provider)
 
 	state = ast_presence_state_helper(provider, &subtype, &message, 0);
 
-	if (state < 0) {
+	if (state == AST_PRESENCE_INVALID) {
 		return;
 	}
 
@@ -393,14 +393,23 @@ int ast_presence_state_engine_init(void)
 static struct ast_manager_event_blob *presence_state_to_ami(struct stasis_message *msg)
 {
 	struct ast_presence_state_message *presence_state = stasis_message_data(msg);
+	struct ast_manager_event_blob *res;
 
-	return ast_manager_event_blob_create(EVENT_FLAG_CALL, "PresenceStateChange",
+	char *subtype = ast_escape_c_alloc(presence_state->subtype);
+	char *message = ast_escape_c_alloc(presence_state->message);
+
+	res = ast_manager_event_blob_create(EVENT_FLAG_CALL, "PresenceStateChange",
 		"Presentity: %s\r\n"
 		"Status: %s\r\n"
 		"Subtype: %s\r\n"
 		"Message: %s\r\n",
 		presence_state->provider,
 		ast_presence_state2str(presence_state->state),
-		presence_state->subtype,
-		presence_state->message);
+		subtype ?: "",
+                message ?: "");
+
+	ast_free(subtype);
+	ast_free(message);
+
+	return res;
 }
diff --git a/main/privacy.c b/main/privacy.c
index 44641eb..8863f09 100644
--- a/main/privacy.c
+++ b/main/privacy.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 369013 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/time.h>
 #include <signal.h>
diff --git a/main/rtp_engine.c b/main/rtp_engine.c
index f029e2b..6ff8f52 100644
--- a/main/rtp_engine.c
+++ b/main/rtp_engine.c
@@ -137,7 +137,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 427763 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <math.h>
 
@@ -170,8 +170,10 @@ struct ast_rtp_instance {
 	int properties[AST_RTP_PROPERTY_MAX];
 	/*! Address that we are expecting RTP to come in to */
 	struct ast_sockaddr local_address;
+	/*! The original source address */
+	struct ast_sockaddr requested_target_address;
 	/*! Address that we are sending RTP to */
-	struct ast_sockaddr remote_address;
+	struct ast_sockaddr incoming_source_address;
 	/*! Instance that we are bridged to if doing remote or local bridging */
 	struct ast_rtp_instance *bridged;
 	/*! Payload and packetization information */
@@ -188,6 +190,10 @@ struct ast_rtp_instance {
 	struct ast_srtp *srtp;
 	/*! Channel unique ID */
 	char channel_uniqueid[AST_MAX_UNIQUEID];
+	/*! Time of last packet sent */
+	time_t last_tx;
+	/*! Time of last packet received */
+	time_t last_rx;
 };
 
 /*! List of RTP engines that are currently registered */
@@ -223,14 +229,17 @@ static int mime_types_len = 0;
  * See http://www.iana.org/assignments/rtp-parameters for a list of
  * assigned values
  */
-static struct ast_rtp_payload_type static_RTP_PT[AST_RTP_MAX_PT];
+static struct ast_rtp_payload_type *static_RTP_PT[AST_RTP_MAX_PT];
 static ast_rwlock_t static_RTP_PT_lock;
 
 /*! \brief \ref stasis topic for RTP related messages */
 static struct stasis_topic *rtp_topic;
 
 
-/*! \internal \brief Destructor for \c ast_rtp_payload_type */
+/*!
+ * \internal
+ * \brief Destructor for \c ast_rtp_payload_type
+ */
 static void rtp_payload_type_dtor(void *obj)
 {
 	struct ast_rtp_payload_type *payload = obj;
@@ -242,7 +251,8 @@ struct ast_rtp_payload_type *ast_rtp_engine_alloc_payload_type(void)
 {
 	struct ast_rtp_payload_type *payload;
 
-	payload = ao2_alloc(sizeof(*payload), rtp_payload_type_dtor);
+	payload = ao2_alloc_options(sizeof(*payload), rtp_payload_type_dtor,
+		AO2_ALLOC_OPT_LOCK_NOLOCK);
 
 	return payload;
 }
@@ -467,20 +477,28 @@ int ast_rtp_instance_set_local_address(struct ast_rtp_instance *instance,
 	return 0;
 }
 
-int ast_rtp_instance_set_remote_address(struct ast_rtp_instance *instance,
-		const struct ast_sockaddr *address)
+int ast_rtp_instance_set_incoming_source_address(struct ast_rtp_instance *instance,
+						 const struct ast_sockaddr *address)
 {
-	ast_sockaddr_copy(&instance->remote_address, address);
+	ast_sockaddr_copy(&instance->incoming_source_address, address);
 
 	/* moo */
 
 	if (instance->engine->remote_address_set) {
-		instance->engine->remote_address_set(instance, &instance->remote_address);
+		instance->engine->remote_address_set(instance, &instance->incoming_source_address);
 	}
 
 	return 0;
 }
 
+int ast_rtp_instance_set_requested_target_address(struct ast_rtp_instance *instance,
+						  const struct ast_sockaddr *address)
+{
+	ast_sockaddr_copy(&instance->requested_target_address, address);
+
+	return ast_rtp_instance_set_incoming_source_address(instance, address);
+}
+
 int ast_rtp_instance_get_and_cmp_local_address(struct ast_rtp_instance *instance,
 		struct ast_sockaddr *address)
 {
@@ -498,21 +516,27 @@ void ast_rtp_instance_get_local_address(struct ast_rtp_instance *instance,
 	ast_sockaddr_copy(address, &instance->local_address);
 }
 
-int ast_rtp_instance_get_and_cmp_remote_address(struct ast_rtp_instance *instance,
+int ast_rtp_instance_get_and_cmp_requested_target_address(struct ast_rtp_instance *instance,
 		struct ast_sockaddr *address)
 {
-	if (ast_sockaddr_cmp(address, &instance->remote_address) != 0) {
-		ast_sockaddr_copy(address, &instance->remote_address);
+	if (ast_sockaddr_cmp(address, &instance->requested_target_address) != 0) {
+		ast_sockaddr_copy(address, &instance->requested_target_address);
 		return 1;
 	}
 
 	return 0;
 }
 
-void ast_rtp_instance_get_remote_address(struct ast_rtp_instance *instance,
-		struct ast_sockaddr *address)
+void ast_rtp_instance_get_incoming_source_address(struct ast_rtp_instance *instance,
+						  struct ast_sockaddr *address)
+{
+	ast_sockaddr_copy(address, &instance->incoming_source_address);
+}
+
+void ast_rtp_instance_get_requested_target_address(struct ast_rtp_instance *instance,
+						   struct ast_sockaddr *address)
 {
-	ast_sockaddr_copy(address, &instance->remote_address);
+	ast_sockaddr_copy(address, &instance->requested_target_address);
 }
 
 void ast_rtp_instance_set_extended_prop(struct ast_rtp_instance *instance, int property, void *value)
@@ -579,6 +603,7 @@ void ast_rtp_codecs_payloads_destroy(struct ast_rtp_codecs *codecs)
 void ast_rtp_codecs_payloads_clear(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance)
 {
 	ast_rtp_codecs_payloads_destroy(codecs);
+	ast_rtp_codecs_payloads_initialize(codecs);
 
 	if (instance && instance->engine && instance->engine->payload_set) {
 		int i;
@@ -586,8 +611,6 @@ void ast_rtp_codecs_payloads_clear(struct ast_rtp_codecs *codecs, struct ast_rtp
 			instance->engine->payload_set(instance, i, 0, NULL, 0);
 		}
 	}
-
-	ast_rtp_codecs_payloads_initialize(codecs);
 }
 
 void ast_rtp_codecs_payloads_copy(struct ast_rtp_codecs *src, struct ast_rtp_codecs *dest, struct ast_rtp_instance *instance)
@@ -609,7 +632,7 @@ void ast_rtp_codecs_payloads_copy(struct ast_rtp_codecs *src, struct ast_rtp_cod
 		}
 		ast_debug(2, "Copying payload %d (%p) from %p to %p\n", i, type, src, dest);
 		ao2_bump(type);
-		AST_VECTOR_INSERT(&dest->payloads, i, type);
+		AST_VECTOR_REPLACE(&dest->payloads, i, type);
 
 		if (instance && instance->engine && instance->engine->payload_set) {
 			instance->engine->payload_set(instance, i, type->asterisk_format, type->format, type->rtp_code);
@@ -624,36 +647,34 @@ void ast_rtp_codecs_payloads_set_m_type(struct ast_rtp_codecs *codecs, struct as
 {
 	struct ast_rtp_payload_type *new_type;
 
-	new_type = ast_rtp_engine_alloc_payload_type();
-	if (!new_type) {
+	if (payload < 0 || payload >= AST_RTP_MAX_PT) {
 		return;
 	}
 
 	ast_rwlock_rdlock(&static_RTP_PT_lock);
-	if (payload < 0 || payload >= AST_RTP_MAX_PT) {
-		ast_rwlock_unlock(&static_RTP_PT_lock);
+	new_type = ao2_bump(static_RTP_PT[payload]);
+	ast_rwlock_unlock(&static_RTP_PT_lock);
+	if (!new_type) {
+		ast_debug(1, "Don't have a default tx payload type %d format for m type on %p\n",
+			payload, codecs);
 		return;
 	}
 
+	ast_debug(1, "Setting tx payload type %d based on m type on %p\n",
+		payload, codecs);
+
 	ast_rwlock_wrlock(&codecs->codecs_lock);
+
 	if (payload < AST_VECTOR_SIZE(&codecs->payloads)) {
 		ao2_t_cleanup(AST_VECTOR_GET(&codecs->payloads, payload), "cleaning up replaced payload type");
 	}
-
-	new_type->asterisk_format = static_RTP_PT[payload].asterisk_format;
-	new_type->rtp_code = static_RTP_PT[payload].rtp_code;
-	new_type->payload = payload;
-	new_type->format = ao2_bump(static_RTP_PT[payload].format);
-
-	ast_debug(1, "Setting payload %d (%p) based on m type on %p\n", payload, new_type, codecs);
-	AST_VECTOR_INSERT(&codecs->payloads, payload, new_type);
+	AST_VECTOR_REPLACE(&codecs->payloads, payload, new_type);
 
 	if (instance && instance->engine && instance->engine->payload_set) {
 		instance->engine->payload_set(instance, payload, new_type->asterisk_format, new_type->format, new_type->rtp_code);
 	}
 
 	ast_rwlock_unlock(&codecs->codecs_lock);
-	ast_rwlock_unlock(&static_RTP_PT_lock);
 }
 
 int ast_rtp_codecs_payloads_set_rtpmap_type_rate(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, int pt,
@@ -664,12 +685,11 @@ int ast_rtp_codecs_payloads_set_rtpmap_type_rate(struct ast_rtp_codecs *codecs,
 	unsigned int i;
 	int found = 0;
 
-	ast_rwlock_rdlock(&mime_types_lock);
 	if (pt < 0 || pt >= AST_RTP_MAX_PT) {
-		ast_rwlock_unlock(&mime_types_lock);
 		return -1; /* bogus payload type */
 	}
 
+	ast_rwlock_rdlock(&mime_types_lock);
 	ast_rwlock_wrlock(&codecs->codecs_lock);
 	for (i = 0; i < mime_types_len; ++i) {
 		const struct ast_rtp_mime_type *t = &ast_rtp_mime_types[i];
@@ -707,11 +727,15 @@ int ast_rtp_codecs_payloads_set_rtpmap_type_rate(struct ast_rtp_codecs *codecs,
 		new_type->rtp_code = t->payload_type.rtp_code;
 		if ((ast_format_cmp(t->payload_type.format, ast_format_g726) == AST_FORMAT_CMP_EQUAL) &&
 				t->payload_type.asterisk_format && (options & AST_RTP_OPT_G726_NONSTANDARD)) {
-			new_type->format = ao2_bump(ast_format_g726_aal2);
+			new_type->format = ast_format_g726_aal2;
 		} else {
-			new_type->format = ao2_bump(t->payload_type.format);
+			new_type->format = t->payload_type.format;
+		}
+		if (new_type->format) {
+			/* SDP parsing automatically increases the reference count */
+			new_type->format = ast_format_parse_sdp_fmtp(new_type->format, "");
 		}
-		AST_VECTOR_INSERT(&codecs->payloads, pt, new_type);
+		AST_VECTOR_REPLACE(&codecs->payloads, pt, new_type);
 
 		if (instance && instance->engine && instance->engine->payload_set) {
 			instance->engine->payload_set(instance, pt, new_type->asterisk_format, new_type->format, new_type->rtp_code);
@@ -744,7 +768,7 @@ void ast_rtp_codecs_payloads_unset(struct ast_rtp_codecs *codecs, struct ast_rtp
 	if (payload < AST_VECTOR_SIZE(&codecs->payloads)) {
 		type = AST_VECTOR_GET(&codecs->payloads, payload);
 		ao2_cleanup(type);
-		AST_VECTOR_INSERT(&codecs->payloads, payload, NULL);
+		AST_VECTOR_REPLACE(&codecs->payloads, payload, NULL);
 	}
 
 	if (instance && instance->engine && instance->engine->payload_set) {
@@ -770,15 +794,8 @@ struct ast_rtp_payload_type *ast_rtp_codecs_get_payload(struct ast_rtp_codecs *c
 	ast_rwlock_unlock(&codecs->codecs_lock);
 
 	if (!type) {
-		type = ast_rtp_engine_alloc_payload_type();
-		if (!type) {
-			return NULL;
-		}
 		ast_rwlock_rdlock(&static_RTP_PT_lock);
-		type->asterisk_format = static_RTP_PT[payload].asterisk_format;
-		type->rtp_code = static_RTP_PT[payload].rtp_code;
-		type->payload = payload;
-		type->format = ao2_bump(static_RTP_PT[payload].format);
+		type = ao2_bump(static_RTP_PT[payload]);
 		ast_rwlock_unlock(&static_RTP_PT_lock);
 	}
 
@@ -789,17 +806,24 @@ int ast_rtp_codecs_payload_replace_format(struct ast_rtp_codecs *codecs, int pay
 {
 	struct ast_rtp_payload_type *type;
 
-	if (payload < 0 || payload >= AST_RTP_MAX_PT) {
+	if (payload < 0 || payload >= AST_RTP_MAX_PT || !format) {
 		return -1;
 	}
 
+	type = ast_rtp_engine_alloc_payload_type();
+	if (!type) {
+		return -1;
+	}
+	ao2_ref(format, +1);
+	type->format = format;
+	type->asterisk_format = 1;
+	type->payload = payload;
+
 	ast_rwlock_wrlock(&codecs->codecs_lock);
 	if (payload < AST_VECTOR_SIZE(&codecs->payloads)) {
-		type = AST_VECTOR_GET(&codecs->payloads, payload);
-		if (type && type->asterisk_format) {
-			ao2_replace(type->format, format);
-		}
+		ao2_cleanup(AST_VECTOR_GET(&codecs->payloads, payload));
 	}
+	AST_VECTOR_REPLACE(&codecs->payloads, payload, type);
 	ast_rwlock_unlock(&codecs->codecs_lock);
 
 	return 0;
@@ -902,12 +926,15 @@ int ast_rtp_codecs_payload_code(struct ast_rtp_codecs *codecs, int asterisk_form
 	if (payload < 0) {
 		ast_rwlock_rdlock(&static_RTP_PT_lock);
 		for (i = 0; i < AST_RTP_MAX_PT; i++) {
-			if (static_RTP_PT[i].asterisk_format && asterisk_format && format &&
-				(ast_format_cmp(format, static_RTP_PT[i].format) != AST_FORMAT_CMP_NOT_EQUAL)) {
+			if (!static_RTP_PT[i]) {
+				continue;
+			}
+			if (static_RTP_PT[i]->asterisk_format && asterisk_format && format &&
+				(ast_format_cmp(format, static_RTP_PT[i]->format) != AST_FORMAT_CMP_NOT_EQUAL)) {
 				payload = i;
 				break;
-			} else if (!static_RTP_PT[i].asterisk_format && !asterisk_format &&
-				(static_RTP_PT[i].rtp_code == code)) {
+			} else if (!static_RTP_PT[i]->asterisk_format && !asterisk_format &&
+				(static_RTP_PT[i]->rtp_code == code)) {
 				payload = i;
 				break;
 			}
@@ -1607,18 +1634,34 @@ int ast_rtp_dtls_cfg_parse(struct ast_rtp_dtls_cfg *dtls_cfg, const char *name,
 		}
 	} else if (!strcasecmp(name, "dtlscertfile")) {
 		ast_free(dtls_cfg->certfile);
+		if (!ast_strlen_zero(value) && !ast_file_is_readable(value)) {
+			ast_log(LOG_ERROR, "%s file %s does not exist or is not readable\n", name, value);
+			return -1;
+		}
 		dtls_cfg->certfile = ast_strdup(value);
 	} else if (!strcasecmp(name, "dtlsprivatekey")) {
 		ast_free(dtls_cfg->pvtfile);
+		if (!ast_strlen_zero(value) && !ast_file_is_readable(value)) {
+			ast_log(LOG_ERROR, "%s file %s does not exist or is not readable\n", name, value);
+			return -1;
+		}
 		dtls_cfg->pvtfile = ast_strdup(value);
 	} else if (!strcasecmp(name, "dtlscipher")) {
 		ast_free(dtls_cfg->cipher);
 		dtls_cfg->cipher = ast_strdup(value);
 	} else if (!strcasecmp(name, "dtlscafile")) {
 		ast_free(dtls_cfg->cafile);
+		if (!ast_strlen_zero(value) && !ast_file_is_readable(value)) {
+			ast_log(LOG_ERROR, "%s file %s does not exist or is not readable\n", name, value);
+			return -1;
+		}
 		dtls_cfg->cafile = ast_strdup(value);
 	} else if (!strcasecmp(name, "dtlscapath") || !strcasecmp(name, "dtlscadir")) {
 		ast_free(dtls_cfg->capath);
+		if (!ast_strlen_zero(value) && !ast_file_is_readable(value)) {
+			ast_log(LOG_ERROR, "%s file %s does not exist or is not readable\n", name, value);
+			return -1;
+		}
 		dtls_cfg->capath = ast_strdup(value);
 	} else if (!strcasecmp(name, "dtlssetup")) {
 		if (!strcasecmp(value, "active")) {
@@ -1643,6 +1686,8 @@ int ast_rtp_dtls_cfg_parse(struct ast_rtp_dtls_cfg *dtls_cfg, const char *name,
 
 void ast_rtp_dtls_cfg_copy(const struct ast_rtp_dtls_cfg *src_cfg, struct ast_rtp_dtls_cfg *dst_cfg)
 {
+	ast_rtp_dtls_cfg_free(dst_cfg);         /* Prevent a double-call leaking memory via ast_strdup */
+
 	dst_cfg->enabled = src_cfg->enabled;
 	dst_cfg->verify = src_cfg->verify;
 	dst_cfg->rekey = src_cfg->rekey;
@@ -1659,20 +1704,15 @@ void ast_rtp_dtls_cfg_copy(const struct ast_rtp_dtls_cfg *src_cfg, struct ast_rt
 void ast_rtp_dtls_cfg_free(struct ast_rtp_dtls_cfg *dtls_cfg)
 {
 	ast_free(dtls_cfg->certfile);
+	dtls_cfg->certfile = NULL;
 	ast_free(dtls_cfg->pvtfile);
+	dtls_cfg->pvtfile = NULL;
 	ast_free(dtls_cfg->cipher);
+	dtls_cfg->cipher = NULL;
 	ast_free(dtls_cfg->cafile);
+	dtls_cfg->cafile = NULL;
 	ast_free(dtls_cfg->capath);
-}
-
-/*! \internal
- * \brief Small helper routine that cleans up entry i in
- * \c static_RTP_PT.
- */
-static void rtp_engine_static_RTP_PT_cleanup(int i)
-{
-	ao2_cleanup(static_RTP_PT[i].format);
-	memset(&static_RTP_PT[i], 0, sizeof(struct ast_rtp_payload_type));
+	dtls_cfg->capath = NULL;
 }
 
 /*! \internal
@@ -1687,12 +1727,16 @@ static void rtp_engine_mime_type_cleanup(int i)
 
 static void set_next_mime_type(struct ast_format *format, int rtp_code, const char *type, const char *subtype, unsigned int sample_rate)
 {
-	int x = mime_types_len;
-	if (ARRAY_LEN(ast_rtp_mime_types) == mime_types_len) {
+	int x;
+
+	ast_rwlock_wrlock(&mime_types_lock);
+
+	x = mime_types_len;
+	if (ARRAY_LEN(ast_rtp_mime_types) <= x) {
+		ast_rwlock_unlock(&mime_types_lock);
 		return;
 	}
 
-	ast_rwlock_wrlock(&mime_types_lock);
 	/* Make sure any previous value in ast_rtp_mime_types is cleaned up */
 	memset(&ast_rtp_mime_types[x], 0, sizeof(struct ast_rtp_mime_type));	
 	if (format) {
@@ -1705,35 +1749,51 @@ static void set_next_mime_type(struct ast_format *format, int rtp_code, const ch
 	ast_copy_string(ast_rtp_mime_types[x].subtype, subtype, sizeof(ast_rtp_mime_types[x].subtype));
 	ast_rtp_mime_types[x].sample_rate = sample_rate;
 	mime_types_len++;
+
 	ast_rwlock_unlock(&mime_types_lock);
 }
 
 static void add_static_payload(int map, struct ast_format *format, int rtp_code)
 {
 	int x;
+	struct ast_rtp_payload_type *type;
+
+	ast_assert(map < ARRAY_LEN(static_RTP_PT));
+
 	ast_rwlock_wrlock(&static_RTP_PT_lock);
 	if (map < 0) {
 		/* find next available dynamic payload slot */
-		for (x = 96; x < 127; x++) {
-			if (!static_RTP_PT[x].asterisk_format && !static_RTP_PT[x].rtp_code) {
+		for (x = AST_RTP_PT_FIRST_DYNAMIC; x < AST_RTP_MAX_PT; ++x) {
+			if (!static_RTP_PT[x]) {
 				map = x;
 				break;
 			}
 		}
+		if (map < 0) {
+			if (format) {
+				ast_log(LOG_WARNING, "No Dynamic RTP mapping available for format %s\n",
+					ast_format_get_name(format));
+			} else {
+				ast_log(LOG_WARNING, "No Dynamic RTP mapping available for RTP code %d\n",
+					rtp_code);
+			}
+			ast_rwlock_unlock(&static_RTP_PT_lock);
+			return;
+		}
 	}
 
-	if (map < 0) {
-		ast_log(LOG_WARNING, "No Dynamic RTP mapping available for format %s\n",
-			ast_format_get_name(format));
-		ast_rwlock_unlock(&static_RTP_PT_lock);
-		return;
-	}
-
-	if (format) {
-		static_RTP_PT[map].asterisk_format = 1;
-		static_RTP_PT[map].format = ao2_bump(format);
-	} else {
-		static_RTP_PT[map].rtp_code = rtp_code;
+	type = ast_rtp_engine_alloc_payload_type();
+	if (type) {
+		if (format) {
+			ao2_ref(format, +1);
+			type->format = format;
+			type->asterisk_format = 1;
+		} else {
+			type->rtp_code = rtp_code;
+		}
+		type->payload = map;
+		ao2_cleanup(static_RTP_PT[map]);
+		static_RTP_PT[map] = type;
 	}
 	ast_rwlock_unlock(&static_RTP_PT_lock);
 }
@@ -1762,8 +1822,10 @@ int ast_rtp_engine_unload_format(struct ast_format *format)
 	ast_rwlock_wrlock(&static_RTP_PT_lock);
 	/* remove everything pertaining to this format id from the lists */
 	for (x = 0; x < AST_RTP_MAX_PT; x++) {
-		if (ast_format_cmp(static_RTP_PT[x].format, format) == AST_FORMAT_CMP_EQUAL) {
-			rtp_engine_static_RTP_PT_cleanup(x);
+		if (static_RTP_PT[x]
+			&& ast_format_cmp(static_RTP_PT[x]->format, format) == AST_FORMAT_CMP_EQUAL) {
+			ao2_ref(static_RTP_PT[x], -1);
+			static_RTP_PT[x] = NULL;
 		}
 	}
 	ast_rwlock_unlock(&static_RTP_PT_lock);
@@ -1775,7 +1837,9 @@ int ast_rtp_engine_unload_format(struct ast_format *format)
 			rtp_engine_mime_type_cleanup(x);
 			continue;
 		}
-		ast_rtp_mime_types[y] = ast_rtp_mime_types[x];
+		if (x != y) {
+			ast_rtp_mime_types[y] = ast_rtp_mime_types[x];
+		}
 		y++;
 	}
 	mime_types_len = y;
@@ -1931,8 +1995,8 @@ static struct ast_json *rtcp_report_to_json(struct stasis_message *msg,
 	if (payload->report->type == AST_RTP_RTCP_SR) {
 		char sec[32];
 		char usec[32];
-		snprintf(sec, sizeof(sec), "%lu", payload->report->sender_information.ntp_timestamp.tv_sec);
-		snprintf(usec, sizeof(usec), "%lu", payload->report->sender_information.ntp_timestamp.tv_usec);
+		snprintf(sec, sizeof(sec), "%lu", (unsigned long)payload->report->sender_information.ntp_timestamp.tv_sec);
+		snprintf(usec, sizeof(usec), "%lu", (unsigned long)payload->report->sender_information.ntp_timestamp.tv_usec);
 		json_rtcp_sender_info = ast_json_pack("{s: s, s: s, s: i, s: i, s: i}",
 				"ntp_timestamp_sec", sec,
 				"ntp_timestamp_usec", usec,
@@ -1944,12 +2008,12 @@ static struct ast_json *rtcp_report_to_json(struct stasis_message *msg,
 		}
 	}
 
-	json_rtcp_report = ast_json_pack("{s: i, s: i, s: i, s: O, s: O}",
+	json_rtcp_report = ast_json_pack("{s: i, s: i, s: i, s: o, s: o}",
 			"ssrc", payload->report->ssrc,
 			"type", payload->report->type,
 			"report_count", payload->report->reception_report_count,
-			"sender_information", json_rtcp_sender_info ? json_rtcp_sender_info : ast_json_null(),
-			"report_blocks", json_rtcp_report_blocks);
+			"sender_information", json_rtcp_sender_info ? ast_json_ref(json_rtcp_sender_info) : ast_json_ref(ast_json_null()),
+			"report_blocks", ast_json_ref(json_rtcp_report_blocks));
 	if (!json_rtcp_report) {
 		return NULL;
 	}
@@ -1961,10 +2025,10 @@ static struct ast_json *rtcp_report_to_json(struct stasis_message *msg,
 		}
 	}
 
-	return ast_json_pack("{s: O, s: O, s: O}",
-		"channel", payload->snapshot ? json_channel : ast_json_null(),
-		"rtcp_report", json_rtcp_report,
-		"blob", payload->blob);
+	return ast_json_pack("{s: o, s: o, s: o}",
+		"channel", payload->snapshot ? ast_json_ref(json_channel) : ast_json_ref(ast_json_null()),
+		"rtcp_report", ast_json_ref(json_rtcp_report),
+		"blob", ast_json_deep_copy(payload->blob));
 }
 
 static void rtp_rtcp_report_dtor(void *obj)
@@ -2050,9 +2114,8 @@ static void rtp_engine_shutdown(void)
 
 	ast_rwlock_wrlock(&static_RTP_PT_lock);
 	for (x = 0; x < AST_RTP_MAX_PT; x++) {
-		if (static_RTP_PT[x].format) {
-			rtp_engine_static_RTP_PT_cleanup(x);
-		}
+		ao2_cleanup(static_RTP_PT[x]);
+		static_RTP_PT[x] = NULL;
 	}
 	ast_rwlock_unlock(&static_RTP_PT_lock);
 
@@ -2062,10 +2125,11 @@ static void rtp_engine_shutdown(void)
 			rtp_engine_mime_type_cleanup(x);
 		}
 	}
+	mime_types_len = 0;
 	ast_rwlock_unlock(&mime_types_lock);
 }
 
-int ast_rtp_engine_init()
+int ast_rtp_engine_init(void)
 {
 	ast_rwlock_init(&mime_types_lock);
 	ast_rwlock_init(&static_RTP_PT_lock);
@@ -2076,7 +2140,7 @@ int ast_rtp_engine_init()
 	}
 	STASIS_MESSAGE_TYPE_INIT(ast_rtp_rtcp_sent_type);
 	STASIS_MESSAGE_TYPE_INIT(ast_rtp_rtcp_received_type);
-	ast_register_atexit(rtp_engine_shutdown);
+	ast_register_cleanup(rtp_engine_shutdown);
 
 	/* Define all the RTP mime types available */
 	set_next_mime_type(ast_format_g723, 0, "audio", "G723", 8000);
@@ -2166,3 +2230,23 @@ int ast_rtp_engine_init()
 
 	return 0;
 }
+
+time_t ast_rtp_instance_get_last_tx(const struct ast_rtp_instance *rtp)
+{
+	return rtp->last_tx;
+}
+
+void ast_rtp_instance_set_last_tx(struct ast_rtp_instance *rtp, time_t time)
+{
+	rtp->last_tx = time;
+}
+
+time_t ast_rtp_instance_get_last_rx(const struct ast_rtp_instance *rtp)
+{
+	return rtp->last_rx;
+}
+
+void ast_rtp_instance_set_last_rx(struct ast_rtp_instance *rtp, time_t time)
+{
+	rtp->last_rx = time;
+}
diff --git a/main/say.c b/main/say.c
index c6d85b1..ef80dfa 100644
--- a/main/say.c
+++ b/main/say.c
@@ -37,7 +37,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 417591 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <netinet/in.h>
 #include <time.h>
diff --git a/main/sched.c b/main/sched.c
index 3106d6d..f851670 100644
--- a/main/sched.c
+++ b/main/sched.c
@@ -30,7 +30,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 425504 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #ifdef DEBUG_SCHEDULER
 #define DEBUG(a) do { \
@@ -62,9 +62,26 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 425504 $")
 
 AST_THREADSTORAGE(last_del_id);
 
+/*!
+ * \brief Scheduler ID holder
+ *
+ * These form a queue on a scheduler context. When a new
+ * scheduled item is created, a sched_id is popped off the
+ * queue and its id is assigned to the new scheduled item.
+ * When the scheduled task is complete, the sched_id on that
+ * task is then pushed to the back of the queue to be re-used
+ * on some future scheduled item.
+ */
+struct sched_id {
+	/*! Immutable ID number that is copied onto the scheduled task */
+	int id;
+	AST_LIST_ENTRY(sched_id) list;
+};
+
 struct sched {
 	AST_LIST_ENTRY(sched) list;
-	int id;                       /*!< ID number of event */
+	/*! The ID that has been popped off the scheduler context's queue */
+	struct sched_id *sched_id;
 	struct timeval when;          /*!< Absolute time event should take place */
 	int resched;                  /*!< When to reschedule */
 	int variable;                 /*!< Use return value from callback to reschedule */
@@ -99,6 +116,10 @@ struct ast_sched_context {
 	AST_LIST_HEAD_NOLOCK(, sched) schedc;   /*!< Cache of unused schedule structures and how many */
 	unsigned int schedccnt;
 #endif
+	/*! Queue of scheduler task IDs to assign */
+	AST_LIST_HEAD_NOLOCK(, sched_id) id_queue;
+	/*! The number of IDs in the id_queue */
+	int id_queue_size;
 };
 
 static void *sched_run(void *data)
@@ -208,6 +229,8 @@ struct ast_sched_context *ast_sched_context_create(void)
 	ast_mutex_init(&tmp->lock);
 	tmp->eventcnt = 1;
 
+	AST_LIST_HEAD_INIT_NOLOCK(&tmp->id_queue);
+
 	if (!(tmp->sched_heap = ast_heap_create(8, sched_time_cmp,
 			offsetof(struct sched, __heap_index)))) {
 		ast_sched_context_destroy(tmp);
@@ -219,6 +242,11 @@ struct ast_sched_context *ast_sched_context_create(void)
 
 static void sched_free(struct sched *task)
 {
+	/* task->sched_id will be NULL most of the time, but when the
+	 * scheduler context shuts down, it will free all scheduled
+	 * tasks, and in that case, the task->sched_id will be non-NULL
+	 */
+	ast_free(task->sched_id);
 	ast_cond_destroy(&task->cond);
 	ast_free(task);
 }
@@ -226,6 +254,7 @@ static void sched_free(struct sched *task)
 void ast_sched_context_destroy(struct ast_sched_context *con)
 {
 	struct sched *s;
+	struct sched_id *sid;
 
 	sched_thread_destroy(con);
 	con->sched_thread = NULL;
@@ -246,12 +275,91 @@ void ast_sched_context_destroy(struct ast_sched_context *con)
 		con->sched_heap = NULL;
 	}
 
+	while ((sid = AST_LIST_REMOVE_HEAD(&con->id_queue, list))) {
+		ast_free(sid);
+	}
+
 	ast_mutex_unlock(&con->lock);
 	ast_mutex_destroy(&con->lock);
 
 	ast_free(con);
 }
 
+#define ID_QUEUE_INCREMENT 16
+
+/*!
+ * \brief Add new scheduler IDs to the queue.
+ *
+ * \retval The number of IDs added to the queue
+ */
+static int add_ids(struct ast_sched_context *con)
+{
+	int new_size;
+	int original_size;
+	int i;
+
+	original_size = con->id_queue_size;
+	/* So we don't go overboard with the mallocs here, we'll just up
+	 * the size of the list by a fixed amount each time instead of
+	 * multiplying the size by any particular factor
+	 */
+	new_size = original_size + ID_QUEUE_INCREMENT;
+	if (new_size < 0) {
+		/* Overflow. Cap it at INT_MAX. */
+		new_size = INT_MAX;
+	}
+	for (i = original_size; i < new_size; ++i) {
+		struct sched_id *new_id;
+
+		new_id = ast_calloc(1, sizeof(*new_id));
+		if (!new_id) {
+			break;
+		}
+
+		/*
+		 * According to the API doxygen a sched ID of 0 is valid.
+		 * Unfortunately, 0 was never returned historically and
+		 * several users incorrectly coded usage of the returned
+		 * sched ID assuming that 0 was invalid.
+		 */
+		new_id->id = ++con->id_queue_size;
+
+		AST_LIST_INSERT_TAIL(&con->id_queue, new_id, list);
+	}
+
+	return con->id_queue_size - original_size;
+}
+
+static int set_sched_id(struct ast_sched_context *con, struct sched *new_sched)
+{
+	if (AST_LIST_EMPTY(&con->id_queue) && (add_ids(con) == 0)) {
+		return -1;
+	}
+
+	new_sched->sched_id = AST_LIST_REMOVE_HEAD(&con->id_queue, list);
+	return 0;
+}
+
+static void sched_release(struct ast_sched_context *con, struct sched *tmp)
+{
+	if (tmp->sched_id) {
+		AST_LIST_INSERT_TAIL(&con->id_queue, tmp->sched_id, list);
+		tmp->sched_id = NULL;
+	}
+
+	/*
+	 * Add to the cache, or just free() if we
+	 * already have too many cache entries
+	 */
+#ifdef SCHED_MAX_CACHE
+	if (con->schedccnt < SCHED_MAX_CACHE) {
+		AST_LIST_INSERT_HEAD(&con->schedc, tmp, list);
+		con->schedccnt++;
+	} else
+#endif
+		sched_free(tmp);
+}
+
 static struct sched *sched_alloc(struct ast_sched_context *con)
 {
 	struct sched *tmp;
@@ -263,30 +371,42 @@ static struct sched *sched_alloc(struct ast_sched_context *con)
 #ifdef SCHED_MAX_CACHE
 	if ((tmp = AST_LIST_REMOVE_HEAD(&con->schedc, list))) {
 		con->schedccnt--;
-	} else 
+	} else
 #endif
 	{
 		tmp = ast_calloc(1, sizeof(*tmp));
+		if (!tmp) {
+			return NULL;
+		}
 		ast_cond_init(&tmp->cond, NULL);
 	}
 
+	if (set_sched_id(con, tmp)) {
+		sched_release(con, tmp);
+		return NULL;
+	}
+
 	return tmp;
 }
 
-static void sched_release(struct ast_sched_context *con, struct sched *tmp)
+void ast_sched_clean_by_callback(struct ast_sched_context *con, ast_sched_cb match, ast_sched_cb cleanup_cb)
 {
-	/*
-	 * Add to the cache, or just free() if we
-	 * already have too many cache entries
-	 */
+	int i = 1;
+	struct sched *current;
 
-#ifdef SCHED_MAX_CACHE
-	if (con->schedccnt < SCHED_MAX_CACHE) {
-		AST_LIST_INSERT_HEAD(&con->schedc, tmp, list);
-		con->schedccnt++;
-	} else
-#endif
-		sched_free(tmp);
+	ast_mutex_lock(&con->lock);
+	while ((current = ast_heap_peek(con->sched_heap, i))) {
+		if (current->callback != match) {
+			i++;
+			continue;
+		}
+
+		ast_heap_remove(con->sched_heap, current);
+
+		cleanup_cb(current->data);
+		sched_release(con, current);
+	}
+	ast_mutex_unlock(&con->lock);
 }
 
 /*! \brief
@@ -337,6 +457,17 @@ static int sched_settime(struct timeval *t, int when)
 {
 	struct timeval now = ast_tvnow();
 
+	if (when < 0) {
+		/*
+		 * A negative when value is likely a bug as it
+		 * represents a VERY large timeout time.
+		 */
+		ast_log(LOG_WARNING,
+			"Bug likely: Negative time interval %d (interpreted as %u ms) requested!\n",
+			when, (unsigned int) when);
+		ast_assert(0);
+	}
+
 	/*ast_debug(1, "TV -> %lu,%lu\n", tv->tv_sec, tv->tv_usec);*/
 	if (ast_tvzero(*t))	/* not supplied, default to now */
 		*t = now;
@@ -368,7 +499,7 @@ int ast_sched_add_variable(struct ast_sched_context *con, int when, ast_sched_cb
 
 	ast_mutex_lock(&con->lock);
 	if ((tmp = sched_alloc(con))) {
-		tmp->id = con->eventcnt++;
+		con->eventcnt++;
 		tmp->callback = callback;
 		tmp->data = data;
 		tmp->resched = when;
@@ -379,7 +510,7 @@ int ast_sched_add_variable(struct ast_sched_context *con, int when, ast_sched_cb
 			sched_release(con, tmp);
 		} else {
 			schedule(con, tmp);
-			res = tmp->id;
+			res = tmp->sched_id->id;
 		}
 	}
 #ifdef DUMP_SCHEDULER
@@ -417,7 +548,7 @@ static struct sched *sched_find(struct ast_sched_context *con, int id)
 	for (x = 1; x <= heap_size; x++) {
 		struct sched *cur = ast_heap_peek(con->sched_heap, x);
 
-		if (cur->id == id) {
+		if (cur->sched_id->id == id) {
 			return cur;
 		}
 	}
@@ -468,16 +599,18 @@ int _ast_sched_del(struct ast_sched_context *con, int id, const char *file, int
 	s = sched_find(con, id);
 	if (s) {
 		if (!ast_heap_remove(con->sched_heap, s)) {
-			ast_log(LOG_WARNING,"sched entry %d not in the sched heap?\n", s->id);
+			ast_log(LOG_WARNING,"sched entry %d not in the sched heap?\n", s->sched_id->id);
 		}
 		sched_release(con, s);
-	} else if (con->currently_executing && (id == con->currently_executing->id)) {
+	} else if (con->currently_executing && (id == con->currently_executing->sched_id->id)) {
 		s = con->currently_executing;
 		s->deleted = 1;
 		/* Wait for executing task to complete so that caller of ast_sched_del() does not
 		 * free memory out from under the task.
 		 */
-		ast_cond_wait(&s->cond, &con->lock);
+		while (con->currently_executing && (id == con->currently_executing->sched_id->id)) {
+			ast_cond_wait(&s->cond, &con->lock);
+		}
 		/* Do not sched_release() here because ast_sched_runq() will do it */
 	}
 
@@ -493,16 +626,8 @@ int _ast_sched_del(struct ast_sched_context *con, int id, const char *file, int
 
 	if (!s && *last_id != id) {
 		ast_debug(1, "Attempted to delete nonexistent schedule entry %d!\n", id);
-#ifndef AST_DEVMODE
-		ast_assert(s != NULL);
-#else
-		{
-			char buf[100];
-
-			snprintf(buf, sizeof(buf), "s != NULL, id=%d", id);
-			_ast_assert(0, buf, file, line, function);
-		}
-#endif
+		/* Removing nonexistent schedule entry shouldn't trigger assert (it was enabled in DEV_MODE);
+		 * because in many places entries is deleted without having valid id. */
 		*last_id = id;
 		return -1;
 	} else if (!s) {
@@ -572,7 +697,7 @@ void ast_sched_dump(struct ast_sched_context *con)
 		q = ast_heap_peek(con->sched_heap, x);
 		delta = ast_tvsub(q->when, when);
 		ast_debug(1, "|%.4d | %-15p | %-15p | %.6ld : %.6ld |\n",
-			q->id,
+			q->sched_id->id,
 			q->callback,
 			q->data,
 			(long)delta.tv_sec,
diff --git a/main/sdp_srtp.c b/main/sdp_srtp.c
index 8b3e0c5..2c49fd2 100644
--- a/main/sdp_srtp.c
+++ b/main/sdp_srtp.c
@@ -32,8 +32,9 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 424647 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
+#include <math.h>
 #include "asterisk/options.h"
 #include "asterisk/utils.h"
 #include "asterisk/sdp_srtp.h"
@@ -68,7 +69,7 @@ void ast_sdp_srtp_destroy(struct ast_sdp_srtp *srtp)
 struct ast_sdp_crypto {
 	char *a_crypto;
 	unsigned char local_key[SRTP_MASTER_LEN];
-	char *tag;
+	int tag;
 	char local_key64[SRTP_MASTER_LEN64];
 	unsigned char remote_key[SRTP_MASTER_LEN];
 };
@@ -79,8 +80,6 @@ void ast_sdp_crypto_destroy(struct ast_sdp_crypto *crypto)
 {
 	ast_free(crypto->a_crypto);
 	crypto->a_crypto = NULL;
-	ast_free(crypto->tag);
-	crypto->tag = NULL;
 	ast_free(crypto);
 }
 
@@ -97,6 +96,7 @@ struct ast_sdp_crypto *ast_sdp_crypto_alloc(void)
 	if (!(p = ast_calloc(1, sizeof(*p)))) {
 		return NULL;
 	}
+	p->tag = 1;
 
 	if (res_srtp->get_random(p->local_key, sizeof(p->local_key)) < 0) {
 		ast_sdp_crypto_destroy(p);
@@ -109,13 +109,13 @@ struct ast_sdp_crypto *ast_sdp_crypto_alloc(void)
 
 	if (key_len != SRTP_MASTER_LEN) {
 		ast_log(LOG_ERROR, "base64 encode/decode bad len %d != %d\n", key_len, SRTP_MASTER_LEN);
-		ast_free(p);
+		ast_sdp_crypto_destroy(p);
 		return NULL;
 	}
 
 	if (memcmp(remote_key, p->local_key, SRTP_MASTER_LEN)) {
 		ast_log(LOG_ERROR, "base64 encode/decode bad key\n");
-		ast_free(p);
+		ast_sdp_crypto_destroy(p);
 		return NULL;
 	}
 
@@ -211,13 +211,15 @@ int ast_sdp_crypto_process(struct ast_rtp_instance *rtp, struct ast_sdp_srtp *sr
 	char *key_params = NULL;
 	char *key_param = NULL;
 	char *session_params = NULL;
-	char *key_salt = NULL;
-	char *lifetime = NULL;
+	char *key_salt = NULL;       /* The actual master key and key salt */
+	char *lifetime = NULL;       /* Key lifetime (# of RTP packets) */
+	char *mki = NULL;            /* Master Key Index */
 	int found = 0;
 	int key_len = 0;
 	int suite_val = 0;
 	unsigned char remote_key[SRTP_MASTER_LEN];
 	int taglen = 0;
+	double sdes_lifetime;
 	struct ast_sdp_crypto *crypto = srtp->crypto;
 
 	if (!ast_rtp_engine_srtp_is_registered()) {
@@ -236,6 +238,12 @@ int ast_sdp_crypto_process(struct ast_rtp_instance *rtp, struct ast_sdp_srtp *sr
 		return -1;
 	}
 
+	/* RFC4568 9.1 - tag is 1-9 digits, greater than zero */
+	if (sscanf(tag, "%30d", &crypto->tag) != 1 || crypto->tag <= 0 || crypto->tag > 999999999) {
+		ast_log(LOG_WARNING, "Unacceptable a=crypto tag: %s\n", tag);
+		return -1;
+	}
+
 	if (!ast_strlen_zero(session_params)) {
 		ast_log(LOG_WARNING, "Unsupported crypto parameters: %s\n", session_params);
 		return -1;
@@ -255,33 +263,88 @@ int ast_sdp_crypto_process(struct ast_rtp_instance *rtp, struct ast_sdp_srtp *sr
 	}
 
 	while ((key_param = strsep(&key_params, ";"))) {
+		unsigned int n_lifetime;
 		char *method = NULL;
 		char *info = NULL;
 
 		method = strsep(&key_param, ":");
 		info = strsep(&key_param, ";");
+		sdes_lifetime = 0;
 
-		if (!strcmp(method, "inline")) {
-			key_salt = strsep(&info, "|");
-			lifetime = strsep(&info, "|");
+		if (strcmp(method, "inline")) {
+			continue;
+		}
 
-			if (lifetime) {
-				ast_log(LOG_NOTICE, "Crypto life time unsupported: %s\n", attr);
-				continue;
-			}
+		key_salt = strsep(&info, "|");
 
+		/* The next parameter can be either lifetime or MKI */
+		lifetime = strsep(&info, "|");
+		if (!lifetime) {
 			found = 1;
 			break;
 		}
+
+		mki = strchr(lifetime, ':');
+		if (mki) {
+			mki = lifetime;
+			lifetime = NULL;
+		} else {
+			mki = strsep(&info, "|");
+		}
+
+		if (mki && *mki != '1') {
+			ast_log(LOG_NOTICE, "Crypto MKI handling is not supported: ignoring attribute %s\n", attr);
+			continue;
+		}
+
+		if (lifetime) {
+			if (!strncmp(lifetime, "2^", 2)) {
+				char *lifetime_val = lifetime + 2;
+
+				/* Exponential lifetime */
+				if (sscanf(lifetime_val, "%30u", &n_lifetime) != 1) {
+					ast_log(LOG_NOTICE, "Failed to parse lifetime value in crypto attribute: %s\n", attr);
+					continue;
+				}
+
+				if (n_lifetime > 48) {
+					/* Yeah... that's a bit big. */
+					ast_log(LOG_NOTICE, "Crypto lifetime exponent of '%u' is a bit large; using 48\n", n_lifetime);
+					n_lifetime = 48;
+				}
+				sdes_lifetime = pow(2, n_lifetime);
+			} else {
+				/* Decimal lifetime */
+				if (sscanf(lifetime, "%30u", &n_lifetime) != 1) {
+					ast_log(LOG_NOTICE, "Failed to parse lifetime value in crypto attribute: %s\n", attr);
+					continue;
+				}
+				sdes_lifetime = n_lifetime;
+			}
+
+			/* Accept anything above 10 hours. Less than 10; reject. */
+			if (sdes_lifetime < 1800000) {
+				ast_log(LOG_NOTICE, "Rejecting crypto attribute '%s': lifetime '%f' too short\n", attr, sdes_lifetime);
+				continue;
+			}
+		}
+
+		ast_debug(2, "Crypto attribute '%s' accepted with lifetime '%f', MKI '%s'\n",
+			attr, sdes_lifetime, mki ? mki : "-");
+
+		found = 1;
+		break;
 	}
 
 	if (!found) {
-		ast_log(LOG_NOTICE, "SRTP crypto offer not acceptable\n");
+		ast_log(LOG_NOTICE, "SRTP crypto offer not acceptable: '%s'\n", attr);
 		return -1;
 	}
 
-	if ((key_len = ast_base64decode(remote_key, key_salt, sizeof(remote_key))) != SRTP_MASTER_LEN) {
-		ast_log(LOG_WARNING, "SRTP descriptions key %d != %d\n", key_len, SRTP_MASTER_LEN);
+	key_len = ast_base64decode(remote_key, key_salt, sizeof(remote_key));
+	if (key_len != SRTP_MASTER_LEN) {
+		ast_log(LOG_WARNING, "SRTP descriptions key length '%d' != master length '%d'\n",
+			key_len, SRTP_MASTER_LEN);
 		return -1;
 	}
 
@@ -296,15 +359,6 @@ int ast_sdp_crypto_process(struct ast_rtp_instance *rtp, struct ast_sdp_srtp *sr
 		return -1;
 	}
 
-	if (!crypto->tag) {
-		ast_debug(1, "Accepting crypto tag %s\n", tag);
-		crypto->tag = ast_strdup(tag);
-		if (!crypto->tag) {
-			ast_log(LOG_ERROR, "Could not allocate memory for tag\n");
-			return -1;
-		}
-	}
-
 	/* Finally, rebuild the crypto line */
 	if (ast_sdp_crypto_build_offer(crypto, taglen)) {
 		return -1;
@@ -321,8 +375,8 @@ int ast_sdp_crypto_build_offer(struct ast_sdp_crypto *p, int taglen)
 		ast_free(p->a_crypto);
 	}
 
-	if (ast_asprintf(&p->a_crypto, "%s AES_CM_128_HMAC_SHA1_%i inline:%s",
-			 p->tag ? p->tag : "1", taglen, p->local_key64) == -1) {
+	if (ast_asprintf(&p->a_crypto, "%d AES_CM_128_HMAC_SHA1_%i inline:%s",
+			 p->tag, taglen, p->local_key64) == -1) {
 			ast_log(LOG_ERROR, "Could not allocate memory for crypto line\n");
 		return -1;
 	}
diff --git a/main/security_events.c b/main/security_events.c
index 2db1ffc..d549d62 100644
--- a/main/security_events.c
+++ b/main/security_events.c
@@ -362,7 +362,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420124 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/utils.h"
 #include "asterisk/strings.h"
@@ -374,7 +374,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420124 $")
 #include "asterisk/json.h"
 #include "asterisk/astobj2.h"
 
-static const size_t TIMESTAMP_STR_LEN = 32;
 static const size_t SECURITY_EVENT_BUF_INIT_LEN = 256;
 
 /*! \brief Security Topic */
@@ -429,7 +428,7 @@ static struct ast_manager_event_blob *security_event_to_ami_blob(struct ast_json
 	event_type_json = ast_json_object_get(json, "SecurityEvent");
 	event_type = ast_json_integer_get(event_type_json);
 
-	ast_assert(event_type >= 0 && event_type < AST_SECURITY_EVENT_NUM_TYPES);
+	ast_assert((unsigned int)event_type < AST_SECURITY_EVENT_NUM_TYPES);
 
 	if (!(str = ast_str_create(SECURITY_EVENT_BUF_INIT_LEN))) {
 		return NULL;
@@ -887,7 +886,7 @@ const char *ast_security_event_severity_get_name(
 
 static int check_event_type(const enum ast_security_event_type event_type)
 {
-	if (event_type < 0 || event_type >= AST_SECURITY_EVENT_NUM_TYPES) {
+	if ((unsigned int)event_type >= AST_SECURITY_EVENT_NUM_TYPES) {
 		ast_log(LOG_ERROR, "Invalid security event type %u\n", event_type);
 		return -1;
 	}
@@ -1173,7 +1172,7 @@ return_error:
 
 int ast_security_event_report(const struct ast_security_event_common *sec)
 {
-	if (sec->event_type < 0 || sec->event_type >= AST_SECURITY_EVENT_NUM_TYPES) {
+	if ((unsigned int)sec->event_type >= AST_SECURITY_EVENT_NUM_TYPES) {
 		ast_log(LOG_ERROR, "Invalid security event type\n");
 		return -1;
 	}
diff --git a/main/sem.c b/main/sem.c
index a7e3d6c..7315165 100644
--- a/main/sem.c
+++ b/main/sem.c
@@ -23,7 +23,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 400186 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/sem.h"
 #include "asterisk/utils.h"
@@ -85,6 +85,7 @@ int ast_sem_post(struct ast_sem *sem)
 
 int ast_sem_wait(struct ast_sem *sem)
 {
+	int res;
 	SCOPED_MUTEX(lock, &sem->mutex);
 
 	ast_assert(sem->count >= 0);
@@ -92,7 +93,37 @@ int ast_sem_wait(struct ast_sem *sem)
 	/* Wait for a non-zero count */
 	++sem->waiters;
 	while (sem->count == 0) {
-		ast_cond_wait(&sem->cond, &sem->mutex);
+		res = ast_cond_wait(&sem->cond, &sem->mutex);
+		/* Give up on error */
+		if (res != 0) {
+			--sem->waiters;
+			return res;
+		}
+	}
+	--sem->waiters;
+
+	/* Take it! */
+	--sem->count;
+
+	return 0;
+}
+
+int ast_sem_timedwait(struct ast_sem *sem, const struct timespec *abs_timeout)
+{
+	int res;
+	SCOPED_MUTEX(lock, &sem->mutex);
+
+	ast_assert(sem->count >= 0);
+
+	/* Wait for a non-zero count */
+	++sem->waiters;
+	while (sem->count == 0) {
+		res = ast_cond_timedwait(&sem->cond, &sem->mutex, abs_timeout);
+		/* Give up on error */
+		if (res != 0) {
+			--sem->waiters;
+			return res;
+		}
 	}
 	--sem->waiters;
 
diff --git a/main/slinfactory.c b/main/slinfactory.c
index bf8a4dd..8c117d6 100644
--- a/main/slinfactory.c
+++ b/main/slinfactory.c
@@ -30,7 +30,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419044 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/frame.h"
 #include "asterisk/format_cache.h"
diff --git a/main/smoother.c b/main/smoother.c
index 4ec5f65..720ad85 100644
--- a/main/smoother.c
+++ b/main/smoother.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419044 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/_private.h"
 #include "asterisk/frame.h"
diff --git a/main/sorcery.c b/main/sorcery.c
index 041bb48..e78fc5c 100644
--- a/main/sorcery.c
+++ b/main/sorcery.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 429000 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/logger.h"
 #include "asterisk/sorcery.h"
@@ -43,6 +43,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 429000 $")
 #include "asterisk/taskprocessor.h"
 #include "asterisk/threadpool.h"
 #include "asterisk/json.h"
+#include "asterisk/vector.h"
 
 /* To prevent DEBUG_FD_LEAKS from interfering with things we undef open and close */
 #undef open
@@ -86,6 +87,35 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 429000 $")
 /*! \brief Thread pool for observers */
 static struct ast_threadpool *threadpool;
 
+/*! \brief Structure for an internal wizard instance */
+struct ast_sorcery_internal_wizard {
+	/*!
+	 * \brief Wizard interface itself
+	 * \warning Callbacks must always be declared first in this structure
+	 * so an ao2_ref on &callbacks will adjust the ref count on
+	 * internal_wizard.
+	 */
+	struct ast_sorcery_wizard callbacks;
+
+	/*! \brief Observers */
+	struct ao2_container *observers;
+};
+
+/*! \brief Structure for a wizard instance which operates on objects */
+struct ast_sorcery_object_wizard {
+	/*! \brief Wizard interface itself */
+	struct ast_sorcery_internal_wizard *wizard;
+
+	/*! \brief Unique data for the wizard */
+	void *data;
+
+	/*! \brief Wizard is acting as an object cache */
+	unsigned int caching:1;
+};
+
+/*! \brief Interface for a sorcery object type wizards */
+AST_VECTOR_RW(ast_sorcery_object_wizards, struct ast_sorcery_object_wizard *);
+
 /*! \brief Structure for internal sorcery object information */
 struct ast_sorcery_object {
 	/*! \brief Unique identifier of this object */
@@ -119,7 +149,7 @@ struct ast_sorcery_object_type {
 	sorcery_diff_handler diff;
 
 	/*! \brief Wizard instances */
-	struct ao2_container *wizards;
+	struct ast_sorcery_object_wizards wizards;
 
 	/*! \brief Object fields */
 	struct ao2_container *fields;
@@ -176,27 +206,6 @@ struct ast_sorcery_object_field {
 	intptr_t args[];
 };
 
-/*! \brief Structure for an internal wizard instance */
-struct ast_sorcery_internal_wizard {
-	/*! \brief Wizard interface itself */
-	struct ast_sorcery_wizard callbacks;
-
-	/*! \brief Observers */
-	struct ao2_container *observers;
-};
-
-/*! \brief Structure for a wizard instance which operates on objects */
-struct ast_sorcery_object_wizard {
-	/*! \brief Wizard interface itself */
-	struct ast_sorcery_internal_wizard *wizard;
-
-	/*! \brief Unique data for the wizard */
-	void *data;
-
-	/*! \brief Wizard is acting as an object cache */
-	unsigned int caching:1;
-};
-
 /*! \brief Full structure for sorcery */
 struct ast_sorcery {
 	/*! \brief Container for known object types */
@@ -295,7 +304,7 @@ static int chararray_handler_fn(const void *obj, const intptr_t *args, char **bu
 
 static int codec_handler_fn(const void *obj, const intptr_t *args, char **buf)
 {
-	struct ast_str *codec_buf = ast_str_alloca(64);
+	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 	struct ast_format_cap **cap = (struct ast_format_cap **)(obj + args[0]);
 	return !(*buf = ast_strdup(ast_format_cap_get_names(*cap, &codec_buf)));
 }
@@ -416,16 +425,11 @@ static int object_type_field_cmp(void *obj, void *arg, int flags)
 	return CMP_MATCH;
 }
 
-/*! \brief Cleanup function */
-static void sorcery_exit(void)
-{
-	ast_threadpool_shutdown(threadpool);
-	threadpool = NULL;
-}
-
 /*! \brief Cleanup function for graceful shutdowns */
 static void sorcery_cleanup(void)
 {
+	ast_threadpool_shutdown(threadpool);
+	threadpool = NULL;
 	ao2_cleanup(wizards);
 	wizards = NULL;
 	ao2_cleanup(observers);
@@ -507,7 +511,6 @@ int ast_sorcery_init(void)
 	observers = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_RWLOCK, 0, NULL, NULL);
 	if (!observers) {
 		sorcery_cleanup();
-		sorcery_exit();
 		return -1;
 	}
 
@@ -515,12 +518,10 @@ int ast_sorcery_init(void)
 		sorcery_instance_hash, sorcery_instance_cmp);
 	if (!instances) {
 		sorcery_cleanup();
-		sorcery_exit();
 		return -1;
 	}
 
 	ast_register_cleanup(sorcery_cleanup);
-	ast_register_atexit(sorcery_exit);
 
 	return 0;
 }
@@ -797,7 +798,10 @@ static void sorcery_object_type_destructor(void *obj)
 {
 	struct ast_sorcery_object_type *object_type = obj;
 
-	ao2_cleanup(object_type->wizards);
+	AST_VECTOR_RW_WRLOCK(&object_type->wizards);
+	AST_VECTOR_CALLBACK_VOID(&object_type->wizards, ao2_cleanup);
+	AST_VECTOR_RW_UNLOCK(&object_type->wizards);
+	AST_VECTOR_RW_FREE(&object_type->wizards);
 	ao2_cleanup(object_type->fields);
 	ao2_cleanup(object_type->observers);
 
@@ -814,6 +818,7 @@ static void sorcery_object_type_destructor(void *obj)
 /*! \brief Internal function which allocates an object type structure */
 static struct ast_sorcery_object_type *sorcery_object_type_alloc(const char *type, const char *module)
 {
+#define INITIAL_WIZARD_VECTOR_SIZE 5
 	struct ast_sorcery_object_type *object_type;
 	char uuid[AST_UUID_STR_LEN];
 
@@ -822,7 +827,7 @@ static struct ast_sorcery_object_type *sorcery_object_type_alloc(const char *typ
 	}
 
 	/* Order matters for object wizards */
-	if (!(object_type->wizards = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, sorcery_wizard_cmp))) {
+	if (AST_VECTOR_RW_INIT(&object_type->wizards, INITIAL_WIZARD_VECTOR_SIZE) != 0) {
 		ao2_ref(object_type, -1);
 		return NULL;
 	}
@@ -872,7 +877,7 @@ static void sorcery_object_wizard_destructor(void *obj)
 {
 	struct ast_sorcery_object_wizard *object_wizard = obj;
 
-	if (object_wizard->data) {
+	if (object_wizard->data && object_wizard->wizard->callbacks.close) {
 		object_wizard->wizard->callbacks.close(object_wizard->data);
 	}
 
@@ -883,16 +888,84 @@ static void sorcery_object_wizard_destructor(void *obj)
 	ao2_cleanup(object_wizard->wizard);
 }
 
-/*! \brief Internal function which creates an object type and adds a wizard mapping */
-enum ast_sorcery_apply_result __ast_sorcery_apply_wizard_mapping(struct ast_sorcery *sorcery,
-		const char *type, const char *module, const char *name, const char *data, unsigned int caching)
+/*! \brief Return the number of wizards mapped to an object type */
+int ast_sorcery_get_wizard_mapping_count(struct ast_sorcery *sorcery,
+	const char *type)
+{
+	RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
+
+	if (!object_type) {
+		return -1;
+	}
+
+	return AST_VECTOR_SIZE(&object_type->wizards);
+}
+
+int ast_sorcery_get_wizard_mapping(struct ast_sorcery *sorcery,
+	const char *type, int index, struct ast_sorcery_wizard **wizard, void **data)
+{
+	RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
+	struct ast_sorcery_object_wizard *owizard;
+
+	if (!object_type) {
+		return -1;
+	}
+
+	if (index < 0 || index >= AST_VECTOR_SIZE(&object_type->wizards)) {
+		return -1;
+	}
+
+	owizard = AST_VECTOR_GET(&object_type->wizards, index);
+
+	if (wizard != NULL) {
+		*wizard = &(owizard->wizard->callbacks);
+		ao2_bump(owizard->wizard);
+	} else {
+		return -1;
+	}
+
+	if (data != NULL) {
+		*data = owizard->data;
+	}
+
+	return 0;
+}
+
+/*! \brief Internal function removes a wizard mapping */
+int __ast_sorcery_remove_wizard_mapping(struct ast_sorcery *sorcery,
+		const char *type, const char *module, const char *name)
+{
+	RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
+	int res;
+
+	if (!object_type) {
+		return -1;
+	}
+
+	AST_VECTOR_RW_WRLOCK(&object_type->wizards);
+#define WIZARD_NAME_COMPARE(a, b) (strcmp((a)->wizard->callbacks.name, (b)) == 0)
+	res = AST_VECTOR_REMOVE_CMP_ORDERED(&object_type->wizards, name, WIZARD_NAME_COMPARE, ao2_cleanup);
+#undef WIZARD_NAME_COMPARE
+	AST_VECTOR_RW_UNLOCK(&object_type->wizards);
+
+	return res;
+}
+
+/*! \brief Internal function which creates an object type and inserts a wizard mapping */
+enum ast_sorcery_apply_result __ast_sorcery_insert_wizard_mapping(struct ast_sorcery *sorcery,
+		const char *type, const char *module, const char *name, const char *data,
+		unsigned int caching, int position)
 {
 	RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
 	RAII_VAR(struct ast_sorcery_internal_wizard *, wizard, ao2_find(wizards, name, OBJ_KEY), ao2_cleanup);
 	RAII_VAR(struct ast_sorcery_object_wizard *, object_wizard, ao2_alloc(sizeof(*object_wizard), sorcery_object_wizard_destructor), ao2_cleanup);
 	int created = 0;
 
-	if (!wizard || !object_wizard) {
+	if (!wizard) {
+		ast_log(LOG_ERROR, "Wizard '%s' could not be applied to object type '%s' as it was not found\n",
+			name, type);
+		return AST_SORCERY_APPLY_FAIL;
+	} else if (!object_wizard) {
 		return AST_SORCERY_APPLY_FAIL;
 	}
 
@@ -903,19 +976,27 @@ enum ast_sorcery_apply_result __ast_sorcery_apply_wizard_mapping(struct ast_sorc
 		created = 1;
 	}
 
+	AST_VECTOR_RW_WRLOCK(&object_type->wizards);
 	if (!created) {
-		struct ast_sorcery_wizard *found;
+		struct ast_sorcery_object_wizard *found;
 
-		found = ao2_find(object_type->wizards, wizard, OBJ_SEARCH_OBJECT);
+#define WIZARD_COMPARE(a, b) ((a)->wizard == (b))
+		found = AST_VECTOR_GET_CMP(&object_type->wizards, wizard, WIZARD_COMPARE);
+#undef WIZARD_COMPARE
 		if (found) {
 			ast_debug(1, "Wizard %s already applied to object type %s\n",
 					wizard->callbacks.name, object_type->name);
-			ao2_cleanup(found);
+			AST_VECTOR_RW_UNLOCK(&object_type->wizards);
 			return AST_SORCERY_APPLY_DUPLICATE;
 		}
 	}
 
+	ast_debug(5, "Calling wizard %s open callback on object type %s\n",
+		name, object_type->name);
 	if (wizard->callbacks.open && !(object_wizard->data = wizard->callbacks.open(data))) {
+		ast_log(LOG_WARNING, "Wizard '%s' failed to open mapping for object type '%s' with data: %s\n",
+			name, object_type->name, S_OR(data, ""));
+		AST_VECTOR_RW_UNLOCK(&object_type->wizards);
 		return AST_SORCERY_APPLY_FAIL;
 	}
 
@@ -924,7 +1005,16 @@ enum ast_sorcery_apply_result __ast_sorcery_apply_wizard_mapping(struct ast_sorc
 	object_wizard->wizard = ao2_bump(wizard);
 	object_wizard->caching = caching;
 
-	ao2_link(object_type->wizards, object_wizard);
+	if (position == AST_SORCERY_WIZARD_POSITION_LAST) {
+		position = AST_VECTOR_SIZE(&object_type->wizards);
+	}
+
+	if (AST_VECTOR_INSERT_AT(&object_type->wizards, position, object_wizard) != 0) {
+		AST_VECTOR_RW_UNLOCK(&object_type->wizards);
+		return AST_SORCERY_APPLY_FAIL;
+	}
+	AST_VECTOR_RW_UNLOCK(&object_type->wizards);
+	ao2_bump(object_wizard);
 
 	if (created) {
 		ao2_link(sorcery->types, object_type);
@@ -936,6 +1026,14 @@ enum ast_sorcery_apply_result __ast_sorcery_apply_wizard_mapping(struct ast_sorc
 	return AST_SORCERY_APPLY_SUCCESS;
 }
 
+/*! \brief Internal function which creates an object type and adds a wizard mapping */
+enum ast_sorcery_apply_result __ast_sorcery_apply_wizard_mapping(struct ast_sorcery *sorcery,
+		const char *type, const char *module, const char *name, const char *data, unsigned int caching)
+{
+	return __ast_sorcery_insert_wizard_mapping(sorcery, type, module, name, data,
+		caching, AST_SORCERY_WIZARD_POSITION_LAST);
+}
+
 enum ast_sorcery_apply_result  __ast_sorcery_apply_config(struct ast_sorcery *sorcery, const char *name, const char *module)
 {
 	struct ast_flags flags = { 0 };
@@ -1012,6 +1110,25 @@ static int sorcery_extended_fields_handler(const void *obj, struct ast_variable
 	return 0;
 }
 
+int ast_sorcery_object_unregister(struct ast_sorcery *sorcery, const char *type)
+{
+	struct ast_sorcery_object_type *object_type;
+	int res = -1;
+
+	ao2_wrlock(sorcery->types);
+	object_type = ao2_find(sorcery->types, type, OBJ_SEARCH_KEY | OBJ_NOLOCK);
+	if (object_type && object_type->type.type == ACO_ITEM) {
+		ao2_unlink_flags(sorcery->types, object_type, OBJ_NOLOCK);
+		res = 0;
+	}
+	ao2_unlock(sorcery->types);
+
+	/* XXX may need to add an instance unregister observer callback on success. */
+
+	ao2_cleanup(object_type);
+	return res;
+}
+
 int __ast_sorcery_object_register(struct ast_sorcery *sorcery, const char *type, unsigned int hidden, unsigned int reloadable, aco_type_item_alloc alloc, sorcery_transform_handler transform, sorcery_apply_handler apply)
 {
 	RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
@@ -1074,6 +1191,7 @@ static void sorcery_object_field_destructor(void *obj)
 
 	if (object_field->name_regex) {
 		regfree(object_field->name_regex);
+		ast_free(object_field->name_regex);
 	}
 }
 
@@ -1176,12 +1294,6 @@ static int sorcery_wizard_load(void *obj, void *arg, int flags)
 	struct sorcery_load_details *details = arg;
 	void (*load)(void *data, const struct ast_sorcery *sorcery, const char *type);
 
-	if (details->reload && !sorcery_reloadable(details->sorcery, details->type)) {
-		ast_log(LOG_NOTICE, "Type '%s' is not reloadable, "
-			"maintaining previous values\n", details->type);
-		return 0;
-	}
-
 	load = !details->reload ? wizard->wizard->callbacks.load : wizard->wizard->callbacks.reload;
 
 	if (load) {
@@ -1254,24 +1366,39 @@ static int sorcery_object_load(void *obj, void *arg, int flags)
 	struct ast_sorcery_object_type *type = obj;
 	struct sorcery_load_details *details = arg;
 
+	if (!type->type.item_alloc) {
+		return 0;
+	}
+
 	details->type = type->name;
 
+	if (details->reload && !sorcery_reloadable(details->sorcery, details->type)) {
+		ast_log(LOG_NOTICE, "Type '%s' is not reloadable, maintaining previous values\n",
+			details->type);
+		return 0;
+	}
+
 	NOTIFY_INSTANCE_OBSERVERS(details->sorcery->observers, object_type_loading,
 		details->sorcery->module_name, details->sorcery, type->name, details->reload);
 
-	ao2_callback(type->wizards, OBJ_NODATA, sorcery_wizard_load, details);
+	AST_VECTOR_RW_RDLOCK(&type->wizards);
+	AST_VECTOR_CALLBACK(&type->wizards, sorcery_wizard_load, NULL, details, 0);
+	AST_VECTOR_RW_UNLOCK(&type->wizards);
+
+	NOTIFY_INSTANCE_OBSERVERS(details->sorcery->observers, object_type_loaded,
+		details->sorcery->module_name, details->sorcery, type->name, details->reload);
 
 	if (ao2_container_count(type->observers)) {
-		struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(type, NULL);
+		struct sorcery_observer_invocation *invocation;
 
-		if (invocation && ast_taskprocessor_push(type->serializer, sorcery_observers_notify_loaded, invocation)) {
+		invocation = sorcery_observer_invocation_alloc(type, NULL);
+		if (invocation
+			&& ast_taskprocessor_push(type->serializer, sorcery_observers_notify_loaded,
+				invocation)) {
 			ao2_cleanup(invocation);
 		}
 	}
 
-	NOTIFY_INSTANCE_OBSERVERS(details->sorcery->observers, object_type_loaded,
-		details->sorcery->module_name, details->sorcery, type->name, details->reload);
-
 	return 0;
 }
 
@@ -1470,10 +1597,13 @@ struct ast_json *ast_sorcery_objectset_json_create(const struct ast_sorcery *sor
 			char *buf = NULL;
 			struct ast_json *value = NULL;
 
-			if ((res = object_field->handler(object, object_field->args, &buf))
+			if (object_field->handler(object, object_field->args, &buf)
 				|| !(value = ast_json_string_create(buf))
 				|| ast_json_object_set(json, object_field->name, value)) {
-				res = -1;
+				ast_free(buf);
+				ast_debug(5, "Skipping field '%s' for object type '%s'\n",
+					object_field->name, object_type->name);
+				continue;
 			}
 
 			ast_free(buf);
@@ -1606,6 +1736,10 @@ void *ast_sorcery_alloc(const struct ast_sorcery *sorcery, const char *type, con
 	} else {
 		details->object->id = ast_strdup(id);
 	}
+	if (!details->object->id) {
+		ao2_ref(details, -1);
+		return NULL;
+	}
 
 	ast_copy_string(details->object->type, type, sizeof(details->object->type));
 
@@ -1696,31 +1830,36 @@ void *ast_sorcery_retrieve_by_id(const struct ast_sorcery *sorcery, const char *
 {
 	RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
 	void *object = NULL;
-	struct ao2_iterator i;
-	struct ast_sorcery_object_wizard *wizard;
+	int i;
 	unsigned int cached = 0;
 
 	if (!object_type || ast_strlen_zero(id)) {
 		return NULL;
 	}
 
-	i = ao2_iterator_init(object_type->wizards, 0);
-	for (; (wizard = ao2_iterator_next(&i)); ao2_ref(wizard, -1)) {
+	AST_VECTOR_RW_RDLOCK(&object_type->wizards);
+	for (i = 0; i < AST_VECTOR_SIZE(&object_type->wizards); i++) {
+		struct ast_sorcery_object_wizard *wizard =
+			AST_VECTOR_GET(&object_type->wizards, i);
+
 		if (wizard->wizard->callbacks.retrieve_id &&
 			!(object = wizard->wizard->callbacks.retrieve_id(sorcery, wizard->data, object_type->name, id))) {
 			continue;
 		}
 
 		cached = wizard->caching;
-
-		ao2_ref(wizard, -1);
 		break;
 	}
-	ao2_iterator_destroy(&i);
 
 	if (!cached && object) {
-		ao2_callback(object_type->wizards, 0, sorcery_cache_create, object);
+		struct sorcery_details sdetails = {
+			.sorcery = sorcery,
+			.obj = object,
+		};
+
+		AST_VECTOR_CALLBACK(&object_type->wizards, sorcery_cache_create, NULL, &sdetails, 0);
 	}
+	AST_VECTOR_RW_UNLOCK(&object_type->wizards);
 
 	return object;
 }
@@ -1729,8 +1868,7 @@ void *ast_sorcery_retrieve_by_fields(const struct ast_sorcery *sorcery, const ch
 {
 	RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
 	void *object = NULL;
-	struct ao2_iterator i;
-	struct ast_sorcery_object_wizard *wizard;
+	int i;
 	unsigned int cached = 0;
 
 	if (!object_type) {
@@ -1744,9 +1882,11 @@ void *ast_sorcery_retrieve_by_fields(const struct ast_sorcery *sorcery, const ch
 		}
 	}
 
-	/* Inquire with the available wizards for retrieval */
-	i = ao2_iterator_init(object_type->wizards, 0);
-	for (; (wizard = ao2_iterator_next(&i)); ao2_ref(wizard, -1)) {
+	AST_VECTOR_RW_RDLOCK(&object_type->wizards);
+	for (i = 0; i < AST_VECTOR_SIZE(&object_type->wizards); i++) {
+		struct ast_sorcery_object_wizard *wizard =
+			AST_VECTOR_GET(&object_type->wizards, i);
+
 		if ((flags & AST_RETRIEVE_FLAG_MULTIPLE)) {
 			if (wizard->wizard->callbacks.retrieve_multiple) {
 				wizard->wizard->callbacks.retrieve_multiple(sorcery, wizard->data, object_type->name, object, fields);
@@ -1757,21 +1897,20 @@ void *ast_sorcery_retrieve_by_fields(const struct ast_sorcery *sorcery, const ch
 			}
 		}
 
-		if ((flags & AST_RETRIEVE_FLAG_MULTIPLE) || !object) {
+		if (((flags & AST_RETRIEVE_FLAG_MULTIPLE) && (!ao2_container_count(object) || !wizard->caching)) || !object) {
 			continue;
 		}
 
 		cached = wizard->caching;
 
-		ao2_ref(wizard, -1);
 		break;
 	}
-	ao2_iterator_destroy(&i);
 
 	/* If we are returning a single object and it came from a non-cache source create it in any caches */
 	if (!(flags & AST_RETRIEVE_FLAG_MULTIPLE) && !cached && object) {
-		ao2_callback(object_type->wizards, 0, sorcery_cache_create, object);
+		AST_VECTOR_CALLBACK(&object_type->wizards, sorcery_cache_create, NULL, object, 0);
 	}
+	AST_VECTOR_RW_UNLOCK(&object_type->wizards);
 
 	return object;
 }
@@ -1780,22 +1919,28 @@ struct ao2_container *ast_sorcery_retrieve_by_regex(const struct ast_sorcery *so
 {
 	RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
 	struct ao2_container *objects;
-	struct ao2_iterator i;
-	struct ast_sorcery_object_wizard *wizard;
+	int i;
 
 	if (!object_type || !(objects = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL))) {
 		return NULL;
 	}
 
-	i = ao2_iterator_init(object_type->wizards, 0);
-	for (; (wizard = ao2_iterator_next(&i)); ao2_ref(wizard, -1)) {
+	AST_VECTOR_RW_RDLOCK(&object_type->wizards);
+	for (i = 0; i < AST_VECTOR_SIZE(&object_type->wizards); i++) {
+		struct ast_sorcery_object_wizard *wizard =
+			AST_VECTOR_GET(&object_type->wizards, i);
+
 		if (!wizard->wizard->callbacks.retrieve_regex) {
 			continue;
 		}
 
 		wizard->wizard->callbacks.retrieve_regex(sorcery, wizard->data, object_type->name, objects, regex);
+
+		if (wizard->caching && ao2_container_count(objects)) {
+			break;
+		}
 	}
-	ao2_iterator_destroy(&i);
+	AST_VECTOR_RW_UNLOCK(&object_type->wizards);
 
 	return objects;
 }
@@ -1807,9 +1952,7 @@ static int sorcery_wizard_create(void *obj, void *arg, int flags)
 	const struct sorcery_details *details = arg;
 
 	if (!object_wizard->wizard->callbacks.create) {
-		ast_assert(0);
-		ast_log(LOG_ERROR, "Sorcery wizard '%s' doesn't contain a 'create' virtual function.\n",
-			object_wizard->wizard->callbacks.name);
+		ast_debug(5, "Sorcery wizard '%s' does not support creation\n", object_wizard->wizard->callbacks.name);
 		return 0;
 	}
 	return (!object_wizard->caching && !object_wizard->wizard->callbacks.create(details->sorcery, object_wizard->data, details->obj)) ? CMP_MATCH | CMP_STOP : 0;
@@ -1842,7 +1985,9 @@ int ast_sorcery_create(const struct ast_sorcery *sorcery, void *object)
 {
 	const struct ast_sorcery_object_details *details = object;
 	RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, details->object->type, OBJ_KEY), ao2_cleanup);
-	RAII_VAR(struct ast_sorcery_object_wizard *, object_wizard, NULL, ao2_cleanup);
+	struct ast_sorcery_object_wizard *object_wizard = NULL;
+	struct ast_sorcery_object_wizard *found_wizard;
+	int i;
 	struct sorcery_details sdetails = {
 		.sorcery = sorcery,
 		.obj = object,
@@ -1852,14 +1997,21 @@ int ast_sorcery_create(const struct ast_sorcery *sorcery, void *object)
 		return -1;
 	}
 
-	if ((object_wizard = ao2_callback(object_type->wizards, 0, sorcery_wizard_create, &sdetails)) &&
-		ao2_container_count(object_type->observers)) {
-		struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(object_type, object);
+	AST_VECTOR_RW_RDLOCK(&object_type->wizards);
+	for (i = 0; i < AST_VECTOR_SIZE(&object_type->wizards); i++) {
+		found_wizard = AST_VECTOR_GET(&object_type->wizards, i);
+		if (sorcery_wizard_create(found_wizard, &sdetails, 0) == (CMP_MATCH | CMP_STOP)) {
+			object_wizard = found_wizard;
+			if(ao2_container_count(object_type->observers)) {
+				struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(object_type, object);
 
-		if (invocation && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_create, invocation)) {
-			ao2_cleanup(invocation);
+				if (invocation && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_create, invocation)) {
+					ao2_cleanup(invocation);
+				}
+			}
 		}
 	}
+	AST_VECTOR_RW_UNLOCK(&object_type->wizards);
 
 	return object_wizard ? 0 : -1;
 }
@@ -1893,7 +2045,12 @@ static int sorcery_wizard_update(void *obj, void *arg, int flags)
 	const struct ast_sorcery_object_wizard *object_wizard = obj;
 	const struct sorcery_details *details = arg;
 
-	return (object_wizard->wizard->callbacks.update && !object_wizard->wizard->callbacks.update(details->sorcery, object_wizard->data, details->obj) &&
+	if (!object_wizard->wizard->callbacks.update) {
+		ast_debug(5, "Sorcery wizard '%s' does not support updating\n", object_wizard->wizard->callbacks.name);
+		return 0;
+	}
+
+	return (!object_wizard->wizard->callbacks.update(details->sorcery, object_wizard->data, details->obj) &&
 		!object_wizard->caching) ? CMP_MATCH | CMP_STOP : 0;
 }
 
@@ -1901,7 +2058,9 @@ int ast_sorcery_update(const struct ast_sorcery *sorcery, void *object)
 {
 	const struct ast_sorcery_object_details *details = object;
 	RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, details->object->type, OBJ_KEY), ao2_cleanup);
-	RAII_VAR(struct ast_sorcery_object_wizard *, object_wizard, NULL, ao2_cleanup);
+	struct ast_sorcery_object_wizard *object_wizard = NULL;
+	struct ast_sorcery_object_wizard *found_wizard;
+	int i;
 	struct sorcery_details sdetails = {
 		.sorcery = sorcery,
 		.obj = object,
@@ -1911,14 +2070,21 @@ int ast_sorcery_update(const struct ast_sorcery *sorcery, void *object)
 		return -1;
 	}
 
-	if ((object_wizard = ao2_callback(object_type->wizards, 0, sorcery_wizard_update, &sdetails)) &&
-		ao2_container_count(object_type->observers)) {
-		struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(object_type, object);
+	AST_VECTOR_RW_RDLOCK(&object_type->wizards);
+	for (i = 0; i < AST_VECTOR_SIZE(&object_type->wizards); i++) {
+		found_wizard = AST_VECTOR_GET(&object_type->wizards, i);
+		if (sorcery_wizard_update(found_wizard, &sdetails, 0) == (CMP_MATCH | CMP_STOP)) {
+			object_wizard = found_wizard;
+			if (ao2_container_count(object_type->observers)) {
+				struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(object_type, object);
 
-		if (invocation && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_update, invocation)) {
-			ao2_cleanup(invocation);
+				if (invocation && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_update, invocation)) {
+					ao2_cleanup(invocation);
+				}
+			}
 		}
 	}
+	AST_VECTOR_RW_UNLOCK(&object_type->wizards);
 
 	return object_wizard ? 0 : -1;
 }
@@ -1952,7 +2118,12 @@ static int sorcery_wizard_delete(void *obj, void *arg, int flags)
 	const struct ast_sorcery_object_wizard *object_wizard = obj;
 	const struct sorcery_details *details = arg;
 
-	return (object_wizard->wizard->callbacks.delete && !object_wizard->wizard->callbacks.delete(details->sorcery, object_wizard->data, details->obj) &&
+	if (!object_wizard->wizard->callbacks.delete) {
+		ast_debug(5, "Sorcery wizard '%s' does not support deletion\n", object_wizard->wizard->callbacks.name);
+		return 0;
+	}
+
+	return (!object_wizard->wizard->callbacks.delete(details->sorcery, object_wizard->data, details->obj) &&
 		!object_wizard->caching) ? CMP_MATCH | CMP_STOP : 0;
 }
 
@@ -1960,7 +2131,9 @@ int ast_sorcery_delete(const struct ast_sorcery *sorcery, void *object)
 {
 	const struct ast_sorcery_object_details *details = object;
 	RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, details->object->type, OBJ_KEY), ao2_cleanup);
-	RAII_VAR(struct ast_sorcery_object_wizard *, object_wizard, NULL, ao2_cleanup);
+	struct ast_sorcery_object_wizard *object_wizard = NULL;
+	struct ast_sorcery_object_wizard *found_wizard;
+	int i;
 	struct sorcery_details sdetails = {
 		.sorcery = sorcery,
 		.obj = object,
@@ -1970,14 +2143,21 @@ int ast_sorcery_delete(const struct ast_sorcery *sorcery, void *object)
 		return -1;
 	}
 
-	if ((object_wizard = ao2_callback(object_type->wizards, 0, sorcery_wizard_delete, &sdetails)) &&
-		ao2_container_count(object_type->observers)) {
-		struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(object_type, object);
+	AST_VECTOR_RW_RDLOCK(&object_type->wizards);
+	for (i = 0; i < AST_VECTOR_SIZE(&object_type->wizards); i++) {
+		found_wizard = AST_VECTOR_GET(&object_type->wizards, i);
+		if (sorcery_wizard_delete(found_wizard, &sdetails, 0) == (CMP_MATCH | CMP_STOP)) {
+			object_wizard = found_wizard;
+			if (ao2_container_count(object_type->observers)) {
+				struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(object_type, object);
 
-		if (invocation && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_delete, invocation)) {
-			ao2_cleanup(invocation);
+				if (invocation && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_delete, invocation)) {
+					ao2_cleanup(invocation);
+				}
+			}
 		}
 	}
+	AST_VECTOR_RW_UNLOCK(&object_type->wizards);
 
 	return object_wizard ? 0 : -1;
 }
@@ -2195,3 +2375,8 @@ int ast_sorcery_is_object_field_registered(const struct ast_sorcery_object_type
 	ao2_cleanup(object_field);
 	return res;
 }
+
+const char *ast_sorcery_get_module(const struct ast_sorcery *sorcery)
+{
+	return sorcery->module_name;
+}
diff --git a/main/sounds_index.c b/main/sounds_index.c
index 8ca5967..c7f9f4d 100644
--- a/main/sounds_index.c
+++ b/main/sounds_index.c
@@ -322,7 +322,7 @@ int ast_sounds_index_init(void)
 		return -1;
 	}
 
-	ast_register_atexit(sounds_cleanup);
+	ast_register_cleanup(sounds_cleanup);
 	return 0;
 }
 
diff --git a/main/srv.c b/main/srv.c
index 2bdd72b..0938a0c 100644
--- a/main/srv.c
+++ b/main/srv.c
@@ -35,7 +35,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 388045 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <netinet/in.h>
 #include <arpa/nameser.h>
diff --git a/main/stasis.c b/main/stasis.c
index aa55350..962efc8 100644
--- a/main/stasis.c
+++ b/main/stasis.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428687 $");
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
 
 #include "asterisk/astobj2.h"
 #include "asterisk/stasis_internal.h"
@@ -444,6 +444,10 @@ static void subscription_invoke(struct stasis_subscription *sub,
 static void send_subscription_subscribe(struct stasis_topic *topic, struct stasis_subscription *sub);
 static void send_subscription_unsubscribe(struct stasis_topic *topic, struct stasis_subscription *sub);
 
+void stasis_subscription_cb_noop(void *data, struct stasis_subscription *sub, struct stasis_message *message)
+{
+}
+
 struct stasis_subscription *internal_stasis_subscribe(
 	struct stasis_topic *topic,
 	stasis_subscription_cb callback,
@@ -1270,8 +1274,8 @@ static struct ast_json *multi_user_event_to_json(
 
 	ast_json_object_set(out, "type", ast_json_string_create("ChannelUserevent"));
 	ast_json_object_set(out, "timestamp", ast_json_timeval(*tv, NULL));
-	ast_json_object_set(out, "eventname", ast_json_ref(ast_json_object_get(blob, "eventname")));
-	ast_json_object_set(out, "userevent", ast_json_ref(blob)); /* eventname gets duplicated, that's ok */
+	ast_json_object_set(out, "eventname", ast_json_string_create(ast_json_string_get((ast_json_object_get(blob, "eventname")))));
+	ast_json_object_set(out, "userevent", ast_json_deep_copy(blob));
 
 	for (type = 0; type < STASIS_UMOS_MAX; ++type) {
 		for (i = 0; i < AST_VECTOR_SIZE(&multi->snapshots[type]); ++i) {
@@ -1533,16 +1537,11 @@ STASIS_MESSAGE_TYPE_DEFN(ast_multi_user_event_type,
 
 /*! @} */
 
-/*! \brief Shutdown function */
-static void stasis_exit(void)
-{
-	ast_threadpool_shutdown(pool);
-	pool = NULL;
-}
-
 /*! \brief Cleanup function for graceful shutdowns */
 static void stasis_cleanup(void)
 {
+	ast_threadpool_shutdown(pool);
+	pool = NULL;
 	STASIS_MESSAGE_TYPE_CLEANUP(stasis_subscription_change_type);
 	STASIS_MESSAGE_TYPE_CLEANUP(ast_multi_user_event_type);
 	aco_info_destroy(&cfg_info);
@@ -1557,7 +1556,6 @@ int stasis_init(void)
 
 	/* Be sure the types are cleaned up after the message bus */
 	ast_register_cleanup(stasis_cleanup);
-	ast_register_atexit(stasis_exit);
 
 	if (aco_info_init(&cfg_info)) {
 		return -1;
diff --git a/main/stasis_bridges.c b/main/stasis_bridges.c
index 3c60a52..1838557 100644
--- a/main/stasis_bridges.c
+++ b/main/stasis_bridges.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 427870 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/astobj2.h"
 #include "asterisk/stasis.h"
@@ -232,7 +232,8 @@ struct ast_bridge_snapshot *ast_bridge_snapshot_create(struct ast_bridge *bridge
 	RAII_VAR(struct ast_bridge_snapshot *, snapshot, NULL, ao2_cleanup);
 	struct ast_bridge_channel *bridge_channel;
 
-	snapshot = ao2_alloc(sizeof(*snapshot), bridge_snapshot_dtor);
+	snapshot = ao2_alloc_options(sizeof(*snapshot), bridge_snapshot_dtor,
+		AO2_ALLOC_OPT_LOCK_NOLOCK);
 	if (!snapshot || ast_string_field_init(snapshot, 128)) {
 		return NULL;
 	}
diff --git a/main/stasis_cache.c b/main/stasis_cache.c
index 98e3965..9129c00 100644
--- a/main/stasis_cache.c
+++ b/main/stasis_cache.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428687 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/astobj2.h"
 #include "asterisk/hashtab.h"
diff --git a/main/stasis_cache_pattern.c b/main/stasis_cache_pattern.c
index 54c7b5a..9e3de36 100644
--- a/main/stasis_cache_pattern.c
+++ b/main/stasis_cache_pattern.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 418997 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/astobj2.h"
 #include "asterisk/stasis_cache_pattern.h"
diff --git a/main/stasis_channels.c b/main/stasis_channels.c
index ccb9ee8..d46a8dd 100644
--- a/main/stasis_channels.c
+++ b/main/stasis_channels.c
@@ -30,7 +30,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 429064 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/astobj2.h"
 #include "asterisk/json.h"
@@ -209,7 +209,8 @@ struct ast_channel_snapshot *ast_channel_snapshot_create(struct ast_channel *cha
 		return NULL;
 	}
 
-	snapshot = ao2_alloc(sizeof(*snapshot), channel_snapshot_dtor);
+	snapshot = ao2_alloc_options(sizeof(*snapshot), channel_snapshot_dtor,
+		AO2_ALLOC_OPT_LOCK_NOLOCK);
 	if (!snapshot || ast_string_field_init(snapshot, 1024)) {
 		ao2_cleanup(snapshot);
 		return NULL;
@@ -363,6 +364,7 @@ static void ast_channel_publish_dial_internal(struct ast_channel *caller,
 }
 
 static void remove_dial_masquerade(struct ast_channel *peer);
+static void remove_dial_masquerade_caller(struct ast_channel *caller);
 static int set_dial_masquerade(struct ast_channel *caller,
 	struct ast_channel *peer, const char *dialstring);
 
@@ -372,6 +374,11 @@ void ast_channel_publish_dial_forward(struct ast_channel *caller, struct ast_cha
 {
 	ast_assert(peer != NULL);
 
+	/* XXX With an early bridge the below dial masquerade datastore code could, theoretically,
+	 * go away as the act of changing the channel during dialing would be done using the bridge
+	 * API itself and not a masquerade.
+	 */
+
 	if (caller) {
 		/*
 		 * Lock two or three channels.
@@ -406,6 +413,7 @@ void ast_channel_publish_dial_forward(struct ast_channel *caller, struct ast_cha
 			ast_channel_unlock(forwarded);
 		}
 		ast_channel_unlock(peer);
+		remove_dial_masquerade_caller(caller);
 		ast_channel_unlock(caller);
 	}
 }
@@ -617,7 +625,8 @@ void ast_multi_channel_blob_add_channel(struct ast_multi_channel_blob *obj, cons
 		return;
 	}
 
-	role_snapshot = ao2_alloc(sizeof(*role_snapshot) + role_len, channel_role_snapshot_dtor);
+	role_snapshot = ao2_alloc_options(sizeof(*role_snapshot) + role_len, channel_role_snapshot_dtor,
+		AO2_ALLOC_OPT_LOCK_NOLOCK);
 	if (!role_snapshot) {
 		return;
 	}
@@ -784,8 +793,12 @@ static struct ast_manager_event_blob *varset_to_ami(struct stasis_message *msg)
 	struct ast_channel_blob *obj = stasis_message_data(msg);
 	const char *variable =
 		ast_json_string_get(ast_json_object_get(obj->blob, "variable"));
-	const char *value =
-		ast_json_string_get(ast_json_object_get(obj->blob, "value"));
+	RAII_VAR(char *, value, ast_escape_c_alloc(
+			 ast_json_string_get(ast_json_object_get(obj->blob, "value"))), ast_free);
+
+	if (!value) {
+		return NULL;
+	}
 
 	if (obj->snapshot) {
 		channel_event_string =
@@ -888,7 +901,7 @@ struct ast_json *ast_channel_snapshot_to_json(
 		/* Broken up into groups of three for readability */
 		"{ s: s, s: s, s: s,"
 		"  s: o, s: o, s: s,"
-		"  s: o, s: o }",
+		"  s: o, s: o, s: s }",
 		/* First line */
 		"id", snapshot->uniqueid,
 		"name", snapshot->name,
@@ -902,7 +915,8 @@ struct ast_json *ast_channel_snapshot_to_json(
 		/* Third line */
 		"dialplan", ast_json_dialplan_cep(
 			snapshot->context, snapshot->exten, snapshot->priority),
-		"creationtime", ast_json_timeval(snapshot->creationtime, NULL));
+		"creationtime", ast_json_timeval(snapshot->creationtime, NULL),
+		"language", snapshot->language);
 
 	return ast_json_ref(json_chan);
 }
@@ -1002,23 +1016,28 @@ static struct ast_json *dtmf_end_to_json(
 	struct ast_channel_snapshot *snapshot = channel_blob->snapshot;
 	const char *direction =
 		ast_json_string_get(ast_json_object_get(blob, "direction"));
+	const char *digit =
+		ast_json_string_get(ast_json_object_get(blob, "digit"));
+	long duration_ms =
+		ast_json_integer_get(ast_json_object_get(blob, "duration_ms"));
 	const struct timeval *tv = stasis_message_timestamp(message);
-	struct ast_json *json_channel = ast_channel_snapshot_to_json(snapshot, sanitize);
+	struct ast_json *json_channel;
 
 	/* Only present received DTMF end events as JSON */
 	if (strcasecmp("Received", direction) != 0) {
 		return NULL;
 	}
 
+	json_channel = ast_channel_snapshot_to_json(snapshot, sanitize);
 	if (!json_channel) {
 		return NULL;
 	}
 
-	return ast_json_pack("{s: s, s: o, s: O, s: O, s: o}",
+	return ast_json_pack("{s: s, s: o, s: s, s: i, s: o}",
 		"type", "ChannelDtmfReceived",
 		"timestamp", ast_json_timeval(*tv, NULL),
-		"digit", ast_json_object_get(blob, "digit"),
-		"duration_ms", ast_json_object_get(blob, "duration_ms"),
+		"digit", digit,
+		"duration_ms", duration_ms,
 		"channel", json_channel);
 }
 
@@ -1042,6 +1061,12 @@ static struct ast_json *dial_to_json(
 {
 	struct ast_multi_channel_blob *payload = stasis_message_data(message);
 	struct ast_json *blob = ast_multi_channel_blob_get_json(payload);
+	const char *dialstatus =
+		ast_json_string_get(ast_json_object_get(blob, "dialstatus"));
+	const char *forward =
+		ast_json_string_get(ast_json_object_get(blob, "forward"));
+	const char *dialstring =
+		ast_json_string_get(ast_json_object_get(blob, "dialstring"));
 	struct ast_json *caller_json = ast_channel_snapshot_to_json(ast_multi_channel_blob_get_channel(payload, "caller"), sanitize);
 	struct ast_json *peer_json = ast_channel_snapshot_to_json(ast_multi_channel_blob_get_channel(payload, "peer"), sanitize);
 	struct ast_json *forwarded_json = ast_channel_snapshot_to_json(ast_multi_channel_blob_get_channel(payload, "forwarded"), sanitize);
@@ -1049,12 +1074,12 @@ static struct ast_json *dial_to_json(
 	const struct timeval *tv = stasis_message_timestamp(message);
 	int res = 0;
 
-	json = ast_json_pack("{s: s, s: o, s: O, s: O, s: O}",
+	json = ast_json_pack("{s: s, s: o, s: s, s: s, s: s}",
 		"type", "Dial",
 		"timestamp", ast_json_timeval(*tv, NULL),
-		"dialstatus", ast_json_object_get(blob, "dialstatus"),
-		"forward", ast_json_object_get(blob, "forward"),
-		"dialstring", ast_json_object_get(blob, "dialstring"));
+		"dialstatus", dialstatus,
+		"forward", forward,
+		"dialstring", dialstring);
 	if (!json) {
 		ast_json_unref(caller_json);
 		ast_json_unref(peer_json);
@@ -1132,6 +1157,47 @@ static struct ast_json *talking_stop_to_json(struct stasis_message *message,
 	return channel_blob_to_json(message, "ChannelTalkingFinished", sanitize);
 }
 
+static struct ast_json *hold_to_json(struct stasis_message *message,
+	const struct stasis_message_sanitizer *sanitize)
+{
+	struct ast_channel_blob *channel_blob = stasis_message_data(message);
+	struct ast_json *blob = channel_blob->blob;
+	struct ast_channel_snapshot *snapshot = channel_blob->snapshot;
+	const char *musicclass = ast_json_string_get(ast_json_object_get(blob, "musicclass"));
+	const struct timeval *tv = stasis_message_timestamp(message);
+	struct ast_json *json_channel;
+
+	json_channel = ast_channel_snapshot_to_json(snapshot, sanitize);
+	if (!json_channel) {
+		return NULL;
+	}
+
+	return ast_json_pack("{s: s, s: o, s: s, s: o}",
+		"type", "ChannelHold",
+		"timestamp", ast_json_timeval(*tv, NULL),
+		"musicclass", S_OR(musicclass, "N/A"),
+		"channel", json_channel);
+}
+
+static struct ast_json *unhold_to_json(struct stasis_message *message,
+	const struct stasis_message_sanitizer *sanitize)
+{
+	struct ast_channel_blob *channel_blob = stasis_message_data(message);
+	struct ast_channel_snapshot *snapshot = channel_blob->snapshot;
+	const struct timeval *tv = stasis_message_timestamp(message);
+	struct ast_json *json_channel;
+
+	json_channel = ast_channel_snapshot_to_json(snapshot, sanitize);
+	if (!json_channel) {
+		return NULL;
+	}
+
+	return ast_json_pack("{s: s, s: o, s: o}",
+		"type", "ChannelUnhold",
+		"timestamp", ast_json_timeval(*tv, NULL),
+		"channel", json_channel);
+}
+
 /*!
  * @{ \brief Define channel message types.
  */
@@ -1150,8 +1216,12 @@ STASIS_MESSAGE_TYPE_DEFN(ast_channel_dtmf_begin_type);
 STASIS_MESSAGE_TYPE_DEFN(ast_channel_dtmf_end_type,
 	.to_json = dtmf_end_to_json,
 	);
-STASIS_MESSAGE_TYPE_DEFN(ast_channel_hold_type);
-STASIS_MESSAGE_TYPE_DEFN(ast_channel_unhold_type);
+STASIS_MESSAGE_TYPE_DEFN(ast_channel_hold_type,
+	.to_json = hold_to_json,
+	);
+STASIS_MESSAGE_TYPE_DEFN(ast_channel_unhold_type,
+	.to_json = unhold_to_json,
+	);
 STASIS_MESSAGE_TYPE_DEFN(ast_channel_chanspy_start_type);
 STASIS_MESSAGE_TYPE_DEFN(ast_channel_chanspy_stop_type);
 STASIS_MESSAGE_TYPE_DEFN(ast_channel_fax_type);
@@ -1277,6 +1347,7 @@ static void dial_target_free(struct dial_target *doomed)
 		return;
 	}
 	ast_free(doomed->dialstring);
+	ast_channel_cleanup(doomed->peer);
 	ast_free(doomed);
 }
 
@@ -1299,7 +1370,6 @@ static void dial_masquerade_datastore_cleanup(struct dial_masquerade_datastore *
 	while ((cur = AST_LIST_REMOVE_HEAD(&masq_data->dialed_peers, list))) {
 		dial_target_free(cur);
 	}
-	masq_data->caller = NULL;
 }
 
 static void dial_masquerade_datastore_remove_chan(struct dial_masquerade_datastore *masq_data, struct ast_channel *chan)
@@ -1349,6 +1419,16 @@ static void dial_masquerade_datastore_destroy(void *data)
 	ao2_ref(data, -1);
 }
 
+/*!
+ * \internal
+ * \brief Datastore destructor for dial_masquerade_datastore
+ */
+static void dial_masquerade_caller_datastore_destroy(void *data)
+{
+	dial_masquerade_datastore_cleanup(data);
+	ao2_ref(data, -1);
+}
+
 static struct ast_datastore *dial_masquerade_datastore_find(struct ast_channel *chan);
 
 static void dial_masquerade_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
@@ -1467,6 +1547,13 @@ static const struct ast_datastore_info dial_masquerade_info = {
 	.chan_breakdown = dial_masquerade_breakdown,
 };
 
+static const struct ast_datastore_info dial_masquerade_caller_info = {
+	.type = "stasis-chan-dial-masq",
+	.destroy = dial_masquerade_caller_datastore_destroy,
+	.chan_fixup = dial_masquerade_fixup,
+	.chan_breakdown = dial_masquerade_breakdown,
+};
+
 /*!
  * \internal
  * \brief Find the dial masquerade datastore on the given channel.
@@ -1477,7 +1564,14 @@ static const struct ast_datastore_info dial_masquerade_info = {
  */
 static struct ast_datastore *dial_masquerade_datastore_find(struct ast_channel *chan)
 {
-	return ast_channel_datastore_find(chan, &dial_masquerade_info, NULL);
+	struct ast_datastore *datastore;
+
+	datastore = ast_channel_datastore_find(chan, &dial_masquerade_info, NULL);
+	if (!datastore) {
+		datastore = ast_channel_datastore_find(chan, &dial_masquerade_caller_info, NULL);
+	}
+
+	return datastore;
 }
 
 /*!
@@ -1496,7 +1590,7 @@ static struct dial_masquerade_datastore *dial_masquerade_datastore_add(
 {
 	struct ast_datastore *datastore;
 
-	datastore = ast_datastore_alloc(&dial_masquerade_info, NULL);
+	datastore = ast_datastore_alloc(!masq_data ? &dial_masquerade_caller_info : &dial_masquerade_info, NULL);
 	if (!datastore) {
 		return NULL;
 	}
@@ -1555,7 +1649,7 @@ static int set_dial_masquerade(struct ast_channel *caller, struct ast_channel *p
 			return -1;
 		}
 	}
-	target->peer = peer;
+	target->peer = ast_channel_ref(peer);
 
 	/* Put peer target into datastore */
 	ao2_lock(masq_data);
@@ -1613,3 +1707,24 @@ static void remove_dial_masquerade(struct ast_channel *peer)
 	ast_channel_datastore_remove(peer, datastore);
 	ast_datastore_free(datastore);
 }
+
+static void remove_dial_masquerade_caller(struct ast_channel *caller)
+{
+	struct ast_datastore *datastore;
+	struct dial_masquerade_datastore *masq_data;
+
+	datastore = dial_masquerade_datastore_find(caller);
+	if (!datastore) {
+		return;
+	}
+
+	masq_data = datastore->data;
+	if (!masq_data || !AST_LIST_EMPTY(&masq_data->dialed_peers)) {
+		return;
+	}
+
+	dial_masquerade_datastore_remove_chan(masq_data, caller);
+
+	ast_channel_datastore_remove(caller, datastore);
+	ast_datastore_free(datastore);
+}
diff --git a/main/stasis_endpoints.c b/main/stasis_endpoints.c
index 693f307..fbca2ad 100644
--- a/main/stasis_endpoints.c
+++ b/main/stasis_endpoints.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420124 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/astobj2.h"
 #include "asterisk/stasis.h"
@@ -71,6 +71,35 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420124 $")
 			</syntax>
 		</managerEventInstance>
 	</managerEvent>
+	<managerEvent language="en_US" name="ContactStatus">
+		<managerEventInstance class="EVENT_FLAG_SYSTEM">
+			<synopsis>Raised when the state of a contact changes.</synopsis>
+			<syntax>
+				<parameter name="URI">
+					<para>This contact's URI.</para>
+				</parameter>
+				<parameter name="ContactStatus">
+					<para>New status of the contact.</para>
+					<enumlist>
+						<enum name="Unknown"/>
+						<enum name="Unreachable"/>
+						<enum name="Reachable"/>
+						<enum name="Created"/>
+						<enum name="Removed"/>
+					</enumlist>
+				</parameter>
+				<parameter name="AOR">
+					<para>The name of the associated aor.</para>
+				</parameter>
+				<parameter name="EndpointName">
+					<para>The name of the associated endpoint.</para>
+				</parameter>
+				<parameter name="RoundtripUsec">
+					<para>The RTT measured during the last qualify.</para>
+				</parameter>
+			</syntax>
+		</managerEventInstance>
+	</managerEvent>
 ***/
 
 static struct stasis_cp_all *endpoint_cache_all;
@@ -95,12 +124,7 @@ struct stasis_topic *ast_endpoint_topic_all_cached(void)
 	return stasis_cp_all_topic_cached(endpoint_cache_all);
 }
 
-static struct ast_manager_event_blob *peerstatus_to_ami(struct stasis_message *msg);
-
 STASIS_MESSAGE_TYPE_DEFN(ast_endpoint_snapshot_type);
-STASIS_MESSAGE_TYPE_DEFN(ast_endpoint_state_type,
-	.to_ami = peerstatus_to_ami,
-);
 
 static struct ast_manager_event_blob *peerstatus_to_ami(struct stasis_message *msg)
 {
@@ -137,6 +161,113 @@ static struct ast_manager_event_blob *peerstatus_to_ami(struct stasis_message *m
 		ast_str_buffer(peerstatus_event_string));
 }
 
+static struct ast_json *peerstatus_to_json(struct stasis_message *msg, const struct stasis_message_sanitizer *sanitize)
+{
+	struct ast_endpoint_blob *obj = stasis_message_data(msg);
+	struct ast_json *json_endpoint;
+	struct ast_json *json_peer;
+	struct ast_json *json_final;
+	const struct timeval *tv = stasis_message_timestamp(msg);
+
+	json_endpoint = ast_endpoint_snapshot_to_json(obj->snapshot, NULL);
+	if (!json_endpoint) {
+		return NULL;
+	}
+
+	json_peer = ast_json_object_create();
+	if (!json_peer) {
+		ast_json_unref(json_endpoint);
+		return NULL;
+	}
+
+	/* Copy all fields from the blob */
+	ast_json_object_update(json_peer, obj->blob);
+
+	json_final = ast_json_pack("{s: s, s: o, s: o, s: o }",
+		"type", "PeerStatusChange",
+		"timestamp", ast_json_timeval(*tv, NULL),
+		"endpoint", json_endpoint,
+		"peer", json_peer);
+	if (!json_final) {
+		ast_json_unref(json_endpoint);
+		ast_json_unref(json_peer);
+	}
+
+	return json_final;
+}
+
+STASIS_MESSAGE_TYPE_DEFN(ast_endpoint_state_type,
+	.to_ami = peerstatus_to_ami,
+	.to_json = peerstatus_to_json,
+);
+
+static struct ast_manager_event_blob *contactstatus_to_ami(struct stasis_message *msg)
+{
+	struct ast_endpoint_blob *obj = stasis_message_data(msg);
+	RAII_VAR(struct ast_str *, contactstatus_event_string, ast_str_create(64), ast_free);
+	const char *value;
+
+	if (!(value = ast_json_string_get(ast_json_object_get(obj->blob, "uri")))) {
+		return NULL;
+	}
+	ast_str_append(&contactstatus_event_string, 0, "URI: %s\r\n", value);
+
+	if (!(value = ast_json_string_get(ast_json_object_get(obj->blob, "contact_status")))) {
+		return NULL;
+	}
+	ast_str_append(&contactstatus_event_string, 0, "ContactStatus: %s\r\n", value);
+
+	if (!(value = ast_json_string_get(ast_json_object_get(obj->blob, "aor")))) {
+		return NULL;
+	}
+	ast_str_append(&contactstatus_event_string, 0, "AOR: %s\r\n", value);
+
+	if (!(value = ast_json_string_get(ast_json_object_get(obj->blob, "endpoint_name")))) {
+		return NULL;
+	}
+	ast_str_append(&contactstatus_event_string, 0, "EndpointName: %s\r\n", value);
+
+	if ((value = ast_json_string_get(ast_json_object_get(obj->blob, "roundtrip_usec")))) {
+		ast_str_append(&contactstatus_event_string, 0, "RoundtripUsec: %s\r\n", value);
+	}
+
+	return ast_manager_event_blob_create(EVENT_FLAG_SYSTEM, "ContactStatus",
+		"%s", ast_str_buffer(contactstatus_event_string));
+}
+
+static struct ast_json *contactstatus_to_json(struct stasis_message *msg, const struct stasis_message_sanitizer *sanitize)
+{
+	struct ast_endpoint_blob *obj = stasis_message_data(msg);
+	struct ast_json *json_endpoint;
+	struct ast_json *json_final;
+	const struct timeval *tv = stasis_message_timestamp(msg);
+
+	json_endpoint = ast_endpoint_snapshot_to_json(obj->snapshot, NULL);
+	if (!json_endpoint) {
+		return NULL;
+	}
+
+	json_final = ast_json_pack("{s: s, s: o, s: o, s: { s: s, s: s, s: s, s: s } } ",
+		"type", "ContactStatusChange",
+		"timestamp", ast_json_timeval(*tv, NULL),
+		"endpoint", json_endpoint,
+		"contact_info",
+		"uri", ast_json_string_get(ast_json_object_get(obj->blob, "uri")),
+		"contact_status", ast_json_string_get(ast_json_object_get(obj->blob, "contact_status")),
+		"aor", ast_json_string_get(ast_json_object_get(obj->blob, "aor")),
+		"roundtrip_usec", ast_json_string_get(ast_json_object_get(obj->blob, "roundtrip_usec")));
+	if (!json_final) {
+		ast_json_unref(json_endpoint);
+	}
+
+	return json_final;
+}
+
+STASIS_MESSAGE_TYPE_DEFN(ast_endpoint_contact_state_type,
+	.to_ami = contactstatus_to_ami,
+	.to_json = contactstatus_to_json
+);
+
 static void endpoint_blob_dtor(void *obj)
 {
 	struct ast_endpoint_blob *event = obj;
@@ -294,6 +425,7 @@ static void endpoints_stasis_cleanup(void)
 {
 	STASIS_MESSAGE_TYPE_CLEANUP(ast_endpoint_snapshot_type);
 	STASIS_MESSAGE_TYPE_CLEANUP(ast_endpoint_state_type);
+	STASIS_MESSAGE_TYPE_CLEANUP(ast_endpoint_contact_state_type);
 
 	ao2_cleanup(endpoint_cache_all);
 	endpoint_cache_all = NULL;
@@ -312,6 +444,7 @@ int ast_endpoint_stasis_init(void)
 
 	res |= STASIS_MESSAGE_TYPE_INIT(ast_endpoint_snapshot_type);
 	res |= STASIS_MESSAGE_TYPE_INIT(ast_endpoint_state_type);
+	res |= STASIS_MESSAGE_TYPE_INIT(ast_endpoint_contact_state_type);
 
 	return res;
 }
diff --git a/main/stasis_message.c b/main/stasis_message.c
index 75a9e5e..c797cdf 100644
--- a/main/stasis_message.c
+++ b/main/stasis_message.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420124 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/astobj2.h"
 #include "asterisk/stasis.h"
diff --git a/main/stasis_message_router.c b/main/stasis_message_router.c
index 0692339..26df76c 100644
--- a/main/stasis_message_router.c
+++ b/main/stasis_message_router.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428687 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/astobj2.h"
 #include "asterisk/stasis_message_router.h"
@@ -255,7 +255,9 @@ void stasis_message_router_unsubscribe(struct stasis_message_router *router)
 		return;
 	}
 
-	stasis_unsubscribe(router->subscription);
+	ao2_lock(router);
+	router->subscription = stasis_unsubscribe(router->subscription);
+	ao2_unlock(router);
 }
 
 void stasis_message_router_unsubscribe_and_join(
diff --git a/main/stasis_system.c b/main/stasis_system.c
index bf7b330..e232b8e 100644
--- a/main/stasis_system.c
+++ b/main/stasis_system.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420124 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/astobj2.h"
 #include "asterisk/stasis.h"
diff --git a/main/stdtime/localtime.c b/main/stdtime/localtime.c
index aa1cb26..4b00520 100644
--- a/main/stdtime/localtime.c
+++ b/main/stdtime/localtime.c
@@ -50,7 +50,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 413589 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <signal.h>
 #include <sys/stat.h>
@@ -187,6 +187,60 @@ struct state {
 	AST_LIST_ENTRY(state) list;
 };
 
+/* extra initialisation for sstate_alloc() */
+#define SP_STACK_FLAG INT_MIN
+#ifdef HAVE_INOTIFY
+#	define SP_STACK_INIT(sp) do { \
+		(sp).wd[0] = SP_STACK_FLAG; \
+	} while (0)
+#	define SP_STACK_CHECK(sp) ((sp)->wd[0] == SP_STACK_FLAG)
+#	define SP_HEAP_INIT(sp) do { \
+		(sp)->wd[0] = -1; \
+		(sp)->wd[1] = -1; \
+	} while (0)
+#	define SP_HEAP_FREE(sp) do {} while (0)
+
+#elif defined(HAVE_KQUEUE)
+#	define SP_STACK_INIT(sp) do { \
+		(sp).fd = SP_STACK_FLAG; \
+	} while (0)
+#	define SP_STACK_CHECK(sp) ((sp)->fd == SP_STACK_FLAG)
+#ifdef HAVE_O_SYMLINK
+#	define SP_HEAP_INIT(sp) do { \
+		(sp)->fd = -1; \
+		(sp)->fds = -1; \
+	} while (0)
+#	define SP_HEAP_FREE(sp) do { \
+	if ( (sp) ) { \
+		kqueue_daemon_freestate(sp); \
+		if ((sp)->fd > -1) { close((sp)->fd); (sp)->fd = -1; } \
+		if ((sp)->fds > -1) { close((sp)->fds); (sp)->fds = -1; } \
+	} \
+	} while (0)
+
+#else  /* HAVE_O_SYMLINK */
+#	define SP_HEAP_INIT(sp) do { \
+		(sp)->fd = -1; \
+		(sp)->dir = NULL; \
+	} while (0)
+#	define SP_HEAP_FREE(sp) do { \
+	if ( (sp) ) { \
+		kqueue_daemon_freestate(sp); \
+		if ((sp)->fd > -1) { close((sp)->fd); (sp)->fd = -1; } \
+		if ((sp)->dir != NULL) { closedir((sp)->dir); (sp)->dir = NULL; } \
+	} \
+	} while (0)
+
+#endif /* HAVE_O_SYMLINK */
+
+#else  /* defined(HAVE_KQUEUE) */
+#	define SP_STACK_INIT(sp) do {} while (0)
+#	define SP_STACK_CHECK(sp) (0)
+#	define SP_HEAP_INIT(sp) do {} while (0)
+#	define SP_HEAP_FREE(sp) do {} while (0)
+
+#endif
+
 struct locale_entry {
 	AST_LIST_ENTRY(locale_entry) list;
 	locale_t locale;
@@ -253,6 +307,9 @@ static int		tzload P((const char * name, struct state * sp,
 				int doextend));
 static int		tzparse P((const char * name, struct state * sp,
 				int lastditch));
+/* struct state allocator with additional setup as needed */
+static struct state *	sstate_alloc(void);
+static void		sstate_free(struct state *p);
 
 static AST_LIST_HEAD_STATIC(zonelist, state);
 #ifdef HAVE_NEWLOCALE
@@ -274,7 +331,20 @@ static void common_startup(void) {
 	struct state *sp;
 	AST_LIST_LOCK(&zonelist);
 	AST_LIST_TRAVERSE(&zonelist, sp, list) {
-		add_notify(sp, sp->name);
+		/* ensure sp->name is not relative -- it
+		 * often is -- otherwise add_notify() fails
+		 */
+		char name[FILENAME_MAX + 1];
+
+		if (sp->name[0] == '/') {
+			snprintf(name, sizeof(name), "%s", sp->name);
+		} else if (!strcmp(sp->name, TZDEFAULT)) {
+			snprintf(name, sizeof(name), "/etc/%s", sp->name);
+		} else {
+			snprintf(name, sizeof(name), "%s/%s", TZDIR, sp->name);
+		}
+
+		add_notify(sp, name);
 	}
 	AST_LIST_UNLOCK(&zonelist);
 }
@@ -284,10 +354,9 @@ static int inotify_fd = -1;
 
 static void *inotify_daemon(void *data)
 {
-	struct {
-		struct inotify_event iev;
-		char name[FILENAME_MAX + 1];
-	} buf;
+	/* inotify_event is dynamically sized */
+	struct inotify_event *iev;
+	size_t real_sizeof_iev = sizeof(*iev) + FILENAME_MAX + 1;
 	ssize_t res;
 	struct state *cur;
 
@@ -302,14 +371,15 @@ static void *inotify_daemon(void *data)
 		inotify_thread = AST_PTHREADT_NULL;
 		return NULL;
 	}
+	iev = ast_alloca(real_sizeof_iev);
 
 	common_startup();
 
 	for (;/*ever*/;) {
 		/* This read should block, most of the time. */
-		if ((res = read(inotify_fd, &buf, sizeof(buf))) < sizeof(buf.iev) && res > 0) {
+		if ((res = read(inotify_fd, iev, real_sizeof_iev)) < sizeof(*iev) && res > 0) {
 			/* This should never happen */
-			ast_log(LOG_ERROR, "Inotify read less than a full event (%zd < %zu)?!!\n", res, sizeof(buf.iev));
+			ast_log(LOG_ERROR, "Inotify read less than a full event (%zd < %zu)?!!\n", res, sizeof(*iev));
 			break;
 		} else if (res < 0) {
 			if (errno == EINTR || errno == EAGAIN) {
@@ -325,9 +395,9 @@ static void *inotify_daemon(void *data)
 		}
 		AST_LIST_LOCK(&zonelist);
 		AST_LIST_TRAVERSE_SAFE_BEGIN(&zonelist, cur, list) {
-			if (cur->wd[0] == buf.iev.wd || cur->wd[1] == buf.iev.wd) {
+			if (cur->wd[0] == iev->wd || cur->wd[1] == iev->wd) {
 				AST_LIST_REMOVE_CURRENT(list);
-				ast_free(cur);
+				sstate_free(cur);
 				break;
 			}
 		}
@@ -342,6 +412,13 @@ static void *inotify_daemon(void *data)
 
 static void add_notify(struct state *sp, const char *path)
 {
+	/* watch for flag indicating stack automatic sp,
+	 * should not be added to watch
+	 */
+	if (SP_STACK_CHECK(sp)) {
+		return;
+	}
+
 	if (inotify_thread == AST_PTHREADT_NULL) {
 		ast_cond_init(&initialization, NULL);
 		ast_mutex_init(&initialization_lock);
@@ -376,14 +453,35 @@ static void add_notify(struct state *sp, const char *path)
 #elif defined(HAVE_KQUEUE)
 static int queue_fd = -1;
 
+/*
+ * static struct state *psx_sp and associated code will guard againt
+ * add_notify() called repeatedly for /usr/share/zoneinfo/posixrules
+ * without zonelist check as a result of some errors
+ * (any code where tzparse() is called if tzload() fails --
+ * tzparse() re-calls tzload() for /usr/share/zoneinfo/posixrules)
+ * the pointer itself is guarded by the zonelist lock
+ */
+static struct state *psx_sp = NULL;
+
+/* collect EVFILT_VNODE fflags in macro;
+ */
+#ifdef NOTE_TRUNCATE
+#	define EVVN_NOTES_BITS \
+	(NOTE_DELETE|NOTE_WRITE|NOTE_EXTEND|NOTE_REVOKE|NOTE_ATTRIB \
+	|NOTE_RENAME|NOTE_LINK|NOTE_TRUNCATE)
+#else
+#	define EVVN_NOTES_BITS \
+	(NOTE_DELETE|NOTE_WRITE|NOTE_EXTEND|NOTE_REVOKE|NOTE_ATTRIB \
+	|NOTE_RENAME|NOTE_LINK)
+#endif
+
 static void *kqueue_daemon(void *data)
 {
 	struct kevent kev;
 	struct state *sp;
-	struct timespec no_wait = { 0, 1 };
 
 	ast_mutex_lock(&initialization_lock);
-	if ((queue_fd = kqueue()) < 0) {
+	if (queue_fd < 0 && (queue_fd = kqueue()) < 0) {
 		/* ast_log uses us to format messages, so if we called ast_log, we'd be
 		 * in for a nasty loop (seen already in testing) */
 		fprintf(stderr, "Unable to initialize kqueue(): %s\n", strerror(errno));
@@ -410,55 +508,104 @@ static void *kqueue_daemon(void *data)
 
 		sp = kev.udata;
 
-		/*!\note
-		 * If the file event fired, then the file was removed, so we'll need
-		 * to reparse the entry.  The directory event is a bit more
-		 * interesting.  Unfortunately, the queue doesn't contain information
-		 * about the file that changed (only the directory itself), so unless
-		 * we kept a record of the directory state before, it's not really
-		 * possible to know what change occurred.  But if we act paranoid and
-		 * just purge the associated file, then it will get reparsed, and
-		 * everything works fine.  It may be more work, but it's a vast
-		 * improvement over the alternative implementation, which is to stat
-		 * the file repeatedly in what is essentially a busy loop. */
 		AST_LIST_LOCK(&zonelist);
-		AST_LIST_REMOVE(&zonelist, sp, list);
+		/* see comment near psx_sp in add_notify() */
+		if (sp == psx_sp) {
+			psx_sp = NULL;
+
+			sstate_free(sp);
+
+			while ((sp = AST_LIST_REMOVE_HEAD(&zonelist, list))) {
+				sstate_free(sp);
+			}
+		} else {
+			AST_LIST_REMOVE(&zonelist, sp, list);
+			sstate_free(sp);
+		}
+
+		/* Just in case the signal was sent late */
+		ast_cond_broadcast(&initialization);
 		AST_LIST_UNLOCK(&zonelist);
+	}
+
+	inotify_thread = AST_PTHREADT_NULL;
+	return NULL;
+}
+
+static void kqueue_daemon_freestate(struct state *sp)
+{
+	struct kevent kev;
+	struct timespec no_wait = { 0, 1 };
 
+	/*!\note
+	 * If the file event fired, then the file was removed, so we'll need
+	 * to reparse the entry.  The directory event is a bit more
+	 * interesting.  Unfortunately, the queue doesn't contain information
+	 * about the file that changed (only the directory itself), so unless
+	 * we kept a record of the directory state before, it's not really
+	 * possible to know what change occurred.  But if we act paranoid and
+	 * just purge the associated file, then it will get reparsed, and
+	 * everything works fine.  It may be more work, but it's a vast
+	 * improvement over the alternative implementation, which is to stat
+	 * the file repeatedly in what is essentially a busy loop. */
+
+	if (sp->fd > -1) {
 		/* If the directory event fired, remove the file event */
 		EV_SET(&kev, sp->fd, EVFILT_VNODE, EV_DELETE, 0, 0, NULL);
 		kevent(queue_fd, &kev, 1, NULL, 0, &no_wait);
-		close(sp->fd);
+	}
 
 #ifdef HAVE_O_SYMLINK
-		if (sp->fds > -1) {
-			/* If the file event fired, remove the symlink event */
-			EV_SET(&kev, sp->fds, EVFILT_VNODE, EV_DELETE, 0, 0, NULL);
-			kevent(queue_fd, &kev, 1, NULL, 0, &no_wait);
-			close(sp->fds);
-		}
+	if (sp->fds > -1) {
+		/* If the file event fired, remove the symlink event */
+		EV_SET(&kev, sp->fds, EVFILT_VNODE, EV_DELETE, 0, 0, NULL);
+		kevent(queue_fd, &kev, 1, NULL, 0, &no_wait);
+	}
 #else
-		if (sp->dir) {
-			/* If the file event fired, remove the directory event */
-			EV_SET(&kev, dirfd(sp->dir), EVFILT_VNODE, EV_DELETE, 0, 0, NULL);
-			kevent(queue_fd, &kev, 1, NULL, 0, &no_wait);
-			closedir(sp->dir);
-		}
-#endif
-		ast_free(sp);
-
-		/* Just in case the signal was sent late */
-		AST_LIST_LOCK(&zonelist);
-		ast_cond_broadcast(&initialization);
-		AST_LIST_UNLOCK(&zonelist);
+	if (sp->dir) {
+		/* If the file event fired, remove the directory event */
+		EV_SET(&kev, dirfd(sp->dir), EVFILT_VNODE, EV_DELETE, 0, 0, NULL);
+		kevent(queue_fd, &kev, 1, NULL, 0, &no_wait);
 	}
+#endif
 }
 
 static void add_notify(struct state *sp, const char *path)
 {
 	struct kevent kev;
 	struct timespec no_wait = { 0, 1 };
-	char watchdir[PATH_MAX + 1] = "";
+	char   watchdir[PATH_MAX + 1] = "";
+
+	/* watch for flag indicating stack automatic sp,
+	 * should not be added to watch
+	 */
+	if (SP_STACK_CHECK(sp) || sp->fd != -1) {
+		return;
+	}
+
+	/* some errors might cause repeated calls to tzload()
+	 * for TZDEFRULES more than once if errors repeat,
+	 * so psx_sp is used to keep just one
+	 */
+	if (!strcmp(path, TZDEFRULES) ||
+	    !strcmp(path, TZDIR "/" TZDEFRULES)) {
+		int lckgot = AST_LIST_TRYLOCK(&zonelist);
+
+		if (lckgot) {
+			return;
+		}
+
+		if (psx_sp != NULL ||
+		   (psx_sp = sstate_alloc()) == NULL) {
+			AST_LIST_UNLOCK(&zonelist);
+			return;
+		}
+
+		ast_copy_string(psx_sp->name, TZDIR "/" TZDEFRULES,
+			sizeof(psx_sp->name));
+		sp = psx_sp;
+		AST_LIST_UNLOCK(&zonelist);
+	}
 
 	if (inotify_thread == AST_PTHREADT_NULL) {
 		ast_cond_init(&initialization, NULL);
@@ -482,7 +629,8 @@ static void add_notify(struct state *sp, const char *path)
 			| O_EVTONLY
 # endif
 			)) >= 0) {
-		EV_SET(&kev, sp->fds, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_ONESHOT, NOTE_WRITE | NOTE_EXTEND | NOTE_DELETE | NOTE_REVOKE | NOTE_ATTRIB, 0, sp);
+		EV_SET(&kev, sp->fds, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_ONESHOT, EVVN_NOTES_BITS, 0, sp);
+		errno = 0;
 		if (kevent(queue_fd, &kev, 1, NULL, 0, &no_wait) < 0 && errno != 0) {
 			/* According to the API docs, we may get -1 return value, due to the
 			 * NULL space for a returned event, but errno should be 0 unless
@@ -518,7 +666,8 @@ static void add_notify(struct state *sp, const char *path)
 		 * Likewise, there's no potential leak of a descriptor.
 		 */
 		EV_SET(&kev, dirfd(sp->dir), EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_ONESHOT,
-				NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | NOTE_REVOKE | NOTE_ATTRIB, 0, sp);
+				EVVN_NOTES_BITS, 0, sp);
+		errno = 0;
 		if (kevent(queue_fd, &kev, 1, NULL, 0, &no_wait) < 0 && errno != 0) {
 			fprintf(stderr, "Unable to watch '%s': %s\n", watchdir, strerror(errno));
 			closedir(sp->dir);
@@ -538,7 +687,8 @@ watch_file:
 		return;
 	}
 
-	EV_SET(&kev, sp->fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_ONESHOT, NOTE_WRITE | NOTE_EXTEND | NOTE_DELETE | NOTE_REVOKE | NOTE_ATTRIB, 0, sp);
+	EV_SET(&kev, sp->fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_ONESHOT, EVVN_NOTES_BITS, 0, sp);
+	errno = 0;
 	if (kevent(queue_fd, &kev, 1, NULL, 0, &no_wait) < 0 && errno != 0) {
 		/* According to the API docs, we may get -1 return value, due to the
 		 * NULL space for a returned event, but errno should be 0 unless
@@ -550,6 +700,7 @@ watch_file:
 	}
 }
 #else
+
 static void *notify_daemon(void *data)
 {
 	struct stat st, lst;
@@ -589,7 +740,7 @@ static void *notify_daemon(void *data)
 					ast_log(LOG_NOTICE, "Removing cached TZ entry '%s' because underlying file changed.\n", name);
 				}
 				AST_LIST_REMOVE_CURRENT(list);
-				ast_free(cur);
+				sstate_free(cur);
 				continue;
 			}
 		}
@@ -623,6 +774,26 @@ static void add_notify(struct state *sp, const char *path)
 }
 #endif
 
+/*
+ * struct state allocator with additional setup as needed
+ */
+static struct state *sstate_alloc(void)
+{
+	struct state *p = ast_calloc(1, sizeof(*p));
+
+	if (p != NULL) {
+		SP_HEAP_INIT(p);
+	}
+
+	return p;
+}
+
+static void sstate_free(struct state *p)
+{
+	SP_HEAP_FREE(p);
+	ast_free(p);
+}
+
 void ast_localtime_wakeup_monitor(struct ast_test *info)
 {
 	if (inotify_thread != AST_PTHREADT_NULL) {
@@ -737,7 +908,8 @@ static int tzload(const char *name, struct state * const sp, const int doextend)
 		}
 	}
 	nread = read(fid, u.buf, sizeof u.buf);
-	if (close(fid) < 0 || nread <= 0)
+	/* comp nread < sizeof u.tzhead against unexpected short files */
+	if (close(fid) < 0 || nread < sizeof u.tzhead)
 		return -1;
 	for (stored = 4; stored <= 8; stored *= 2) {
 		int		ttisstdcnt;
@@ -864,6 +1036,11 @@ static int tzload(const char *name, struct state * const sp, const int doextend)
 		nread -= p - u.buf;
 		for (i = 0; i < nread; ++i)
 			u.buf[i] = p[i];
+		/* next loop iter. will assume at least
+		   sizeof(struct tzhead) bytes */
+		if (nread < sizeof(u.tzhead)) {
+			break;
+		}
 		/*
 		** If this is a narrow integer time_t system, we're done.
 		*/
@@ -876,6 +1053,12 @@ static int tzload(const char *name, struct state * const sp, const int doextend)
 			struct state	ts;
 			int	result;
 
+			/* for temporary struct state --
+			 * macro flags the the struct as a stack temp.
+			 * to prevent use within add_notify()
+			 */
+			SP_STACK_INIT(ts);
+
 			u.buf[nread - 1] = '\0';
 			result = tzparse(&u.buf[1], &ts, FALSE);
 			if (result == 0 && ts.typecnt == 2 &&
@@ -1443,7 +1626,7 @@ void clean_time_zones(void)
 
 	AST_LIST_LOCK(&zonelist);
 	while ((sp = AST_LIST_REMOVE_HEAD(&zonelist, list))) {
-		ast_free(sp);
+		sstate_free(sp);
 	}
 	AST_LIST_UNLOCK(&zonelist);
 }
@@ -1470,17 +1653,17 @@ static const struct state *ast_tzset(const char *zone)
 			return sp;
 		}
 	}
-	AST_LIST_UNLOCK(&zonelist);
 
-	if (!(sp = ast_calloc(1, sizeof *sp)))
+	if (!(sp = sstate_alloc())) {
+		AST_LIST_UNLOCK(&zonelist);
 		return NULL;
+	}
 
 	if (tzload(zone, sp, TRUE) != 0) {
 		if (zone[0] == ':' || tzparse(zone, sp, FALSE) != 0)
 			(void) gmtload(sp);
 	}
 	ast_copy_string(sp->name, zone, sizeof(sp->name));
-	AST_LIST_LOCK(&zonelist);
 	AST_LIST_INSERT_TAIL(&zonelist, sp, list);
 	AST_LIST_UNLOCK(&zonelist);
 	return sp;
@@ -1724,8 +1907,10 @@ static struct ast_tm *gmtsub(const struct timeval *timep, const long offset, str
 	}
 
 	if (!sp) {
-		if (!(sp = (struct state *) ast_calloc(1, sizeof *sp)))
+		if (!(sp = sstate_alloc())) {
+			AST_LIST_UNLOCK(&zonelist);
 			return NULL;
+		}
 		gmtload(sp);
 		AST_LIST_INSERT_TAIL(&zonelist, sp, list);
 	}
diff --git a/main/strings.c b/main/strings.c
index 299bf68..53d5095 100644
--- a/main/strings.c
+++ b/main/strings.c
@@ -37,7 +37,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420384 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/strings.h"
 #include "asterisk/pbx.h"
@@ -60,58 +60,78 @@ int __ast_str_helper(struct ast_str **buf, ssize_t max_len,
 	int append, const char *fmt, va_list ap)
 #endif
 {
-	int res, need;
+	int res;
+	int added;
+	int need;
 	int offset = (append && (*buf)->__AST_STR_LEN) ? (*buf)->__AST_STR_USED : 0;
 	va_list aq;
 
+	if (max_len < 0) {
+		max_len = (*buf)->__AST_STR_LEN;	/* don't exceed the allocated space */
+	}
+
 	do {
-		if (max_len < 0) {
-			max_len = (*buf)->__AST_STR_LEN;	/* don't exceed the allocated space */
-		}
-		/*
-		 * Ask vsnprintf how much space we need. Remember that vsnprintf
-		 * does not count the final <code>'\\0'</code> so we must add 1.
-		 */
 		va_copy(aq, ap);
 		res = vsnprintf((*buf)->__AST_STR_STR + offset, (*buf)->__AST_STR_LEN - offset, fmt, aq);
+		va_end(aq);
+
+		if (res < 0) {
+			/*
+			 * vsnprintf write to string failed.
+			 * I don't think this is possible with a memory buffer.
+			 */
+			res = AST_DYNSTR_BUILD_FAILED;
+			added = 0;
+			break;
+		}
 
-		need = res + offset + 1;
 		/*
-		 * If there is not enough space and we are below the max length,
-		 * reallocate the buffer and return a message telling to retry.
+		 * vsnprintf returns how much space we used or would need.
+		 * Remember that vsnprintf does not count the nil terminator
+		 * so we must add 1.
 		 */
-		if (need > (*buf)->__AST_STR_LEN && (max_len == 0 || (*buf)->__AST_STR_LEN < max_len) ) {
-			int len = (int)(*buf)->__AST_STR_LEN;
-			if (max_len && max_len < need) {	/* truncate as needed */
-				need = max_len;
-			} else if (max_len == 0) {	/* if unbounded, give more room for next time */
-				need += 16 + need / 4;
-			}
-			if (0) {	/* debugging */
-				ast_verbose("extend from %d to %d\n", len, need);
-			}
-			if (
+		added = res;
+		need = offset + added + 1;
+		if (need <= (*buf)->__AST_STR_LEN
+			|| (max_len && max_len <= (*buf)->__AST_STR_LEN)) {
+			/*
+			 * There was enough room for the string or we are not
+			 * allowed to try growing the string buffer.
+			 */
+			break;
+		}
+
+		/* Reallocate the buffer and try again. */
+		if (max_len == 0) {
+			/* unbounded, give more room for next time */
+			need += 16 + need / 4;
+		} else if (max_len < need) {
+			/* truncate as needed */
+			need = max_len;
+		}
+
+		if (
 #if (defined(MALLOC_DEBUG) && !defined(STANDALONE))
-					_ast_str_make_space(buf, need, file, lineno, function)
+			_ast_str_make_space(buf, need, file, lineno, function)
 #else
-					ast_str_make_space(buf, need)
+			ast_str_make_space(buf, need)
 #endif
-				) {
-				ast_verbose("failed to extend from %d to %d\n", len, need);
-				va_end(aq);
-				return AST_DYNSTR_BUILD_FAILED;
-			}
-			(*buf)->__AST_STR_STR[offset] = '\0';	/* Truncate the partial write. */
+			) {
+			ast_log_safe(LOG_VERBOSE, "failed to extend from %d to %d\n",
+				(int) (*buf)->__AST_STR_LEN, need);
 
-			/* Restart va_copy before calling vsnprintf() again. */
-			va_end(aq);
-			continue;
+			res = AST_DYNSTR_BUILD_FAILED;
+			break;
 		}
-		va_end(aq);
-		break;
 	} while (1);
-	/* update space used, keep in mind the truncation */
-	(*buf)->__AST_STR_USED = (res + offset > (*buf)->__AST_STR_LEN) ? (*buf)->__AST_STR_LEN - 1: res + offset;
+
+	/* Update space used, keep in mind truncation may be necessary. */
+	(*buf)->__AST_STR_USED = ((*buf)->__AST_STR_LEN <= offset + added)
+		? (*buf)->__AST_STR_LEN - 1
+		: offset + added;
+
+	/* Ensure that the string is terminated. */
+	(*buf)->__AST_STR_STR[(*buf)->__AST_STR_USED] = '\0';
 
 	return res;
 }
@@ -170,7 +190,8 @@ static int str_cmp(void *lhs, void *rhs, int flags)
 	return strcmp(lhs, rhs) ? 0 : CMP_MATCH;
 }
 
-struct ao2_container *ast_str_container_alloc_options(enum ao2_container_opts opts, int buckets)
+//struct ao2_container *ast_str_container_alloc_options(enum ao2_container_opts opts, int buckets)
+struct ao2_container *ast_str_container_alloc_options(enum ao2_alloc_opts opts, int buckets)
 {
 	return ao2_container_alloc_options(opts, buckets, str_hash, str_cmp);
 }
diff --git a/main/stun.c b/main/stun.c
index ebe6424..f5bdc9a 100644
--- a/main/stun.c
+++ b/main/stun.c
@@ -32,7 +32,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 427876 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/_private.h"
 #include "asterisk/stun.h"
@@ -516,5 +516,5 @@ static void stun_shutdown(void)
 void ast_stun_init(void)
 {
 	ast_cli_register_multiple(cli_stun, sizeof(cli_stun) / sizeof(struct ast_cli_entry));
-	ast_register_atexit(stun_shutdown);
+	ast_register_cleanup(stun_shutdown);
 }
diff --git a/main/syslog.c b/main/syslog.c
index 55ada23..51da69a 100644
--- a/main/syslog.c
+++ b/main/syslog.c
@@ -30,7 +30,7 @@
 #include "asterisk/utils.h"
 #include "asterisk/syslog.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 369013 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <syslog.h>
 
@@ -163,8 +163,16 @@ static const int logger_level_to_syslog_map[] = {
 
 int ast_syslog_priority_from_loglevel(int level)
 {
+	/* First 16 levels are reserved for system use.
+	 * Default to using LOG_NOTICE for dynamic logging.
+	 */
+	if (level >= 16 && level < ASTNUMLOGLEVELS) {
+		return LOG_NOTICE;
+	}
+
 	if (level < 0 || level >= ARRAY_LEN(logger_level_to_syslog_map)) {
 		return -1;
 	}
+
 	return logger_level_to_syslog_map[level];
 }
diff --git a/main/taskprocessor.c b/main/taskprocessor.c
index 77f5607..7c50089 100644
--- a/main/taskprocessor.c
+++ b/main/taskprocessor.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 424472 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/_private.h"
 #include "asterisk/module.h"
@@ -83,6 +83,8 @@ struct ast_taskprocessor {
 	pthread_t thread;
 	/*! Indicates if the taskprocessor is currently executing a task */
 	unsigned int executing:1;
+	/*! Indicates that a high water warning has been issued on this task processor */
+	unsigned int high_water_warned:1;
 };
 
 /*!
@@ -128,9 +130,6 @@ static int tps_ping_handler(void *datap);
 /*! \brief Remove the front task off the taskprocessor queue */
 static struct tps_task *tps_taskprocessor_pop(struct ast_taskprocessor *tps);
 
-/*! \brief Return the size of the taskprocessor queue */
-static int tps_taskprocessor_depth(struct ast_taskprocessor *tps);
-
 static char *cli_tps_ping(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
 static char *cli_tps_report(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
 
@@ -278,7 +277,7 @@ int ast_tps_init(void)
 
 	ast_cli_register_multiple(taskprocessor_clis, ARRAY_LEN(taskprocessor_clis));
 
-	ast_register_atexit(tps_shutdown);
+	ast_register_cleanup(tps_shutdown);
 
 	return 0;
 }
@@ -506,7 +505,7 @@ static struct tps_task *tps_taskprocessor_pop(struct ast_taskprocessor *tps)
 	return task;
 }
 
-static int tps_taskprocessor_depth(struct ast_taskprocessor *tps)
+long ast_taskprocessor_size(struct ast_taskprocessor *tps)
 {
 	return (tps) ? tps->tps_queue_size : -1;
 }
@@ -593,7 +592,6 @@ static struct ast_taskprocessor *__allocate_taskprocessor(const char *name, stru
 		return NULL;
 	}
 	if (!(p->name = ast_strdup(name))) {
-		ao2_ref(p, -1);
 		return NULL;
 	}
 
@@ -692,15 +690,25 @@ void *ast_taskprocessor_unreference(struct ast_taskprocessor *tps)
 		return NULL;
 	}
 
+	/* To prevent another thread from finding and getting a reference to this
+	 * taskprocessor we hold the singletons lock. If we didn't do this then
+	 * they may acquire it and find that the listener has been shut down.
+	 */
+	ao2_lock(tps_singletons);
+
 	if (ao2_ref(tps, -1) > 3) {
+		ao2_unlock(tps_singletons);
 		return NULL;
 	}
+
 	/* If we're down to 3 references, then those must be:
 	 * 1. The reference we just got rid of
 	 * 2. The container
 	 * 3. The listener
 	 */
-	ao2_unlink(tps_singletons, tps);
+	ao2_unlink_flags(tps_singletons, tps, OBJ_NOLOCK);
+	ao2_unlock(tps_singletons);
+
 	listener_shutdown(tps->listener);
 	return NULL;
 }
@@ -724,6 +732,13 @@ static int taskprocessor_push(struct ast_taskprocessor *tps, struct tps_task *t)
 	ao2_lock(tps);
 	AST_LIST_INSERT_TAIL(&tps->tps_queue, t, list);
 	previous_size = tps->tps_queue_size++;
+
+	if (previous_size >= AST_TASKPROCESSOR_HIGH_WATER_LEVEL && !tps->high_water_warned) {
+		ast_log(LOG_WARNING, "The '%s' task processor queue reached %d scheduled tasks.\n",
+			tps->name, previous_size);
+		tps->high_water_warned = 1;
+	}
+
 	/* The currently executing task counts as still in queue */
 	was_empty = tps->executing ? 0 : previous_size == 0;
 	ao2_unlock(tps);
@@ -745,7 +760,7 @@ int ast_taskprocessor_execute(struct ast_taskprocessor *tps)
 {
 	struct ast_taskprocessor_local local;
 	struct tps_task *t;
-	int size;
+	long size;
 
 	ao2_lock(tps);
 	t = tps_taskprocessor_pop(tps);
@@ -777,7 +792,7 @@ int ast_taskprocessor_execute(struct ast_taskprocessor *tps)
 	 * after we pop an empty stack.
 	 */
 	tps->executing = 0;
-	size = tps_taskprocessor_depth(tps);
+	size = ast_taskprocessor_size(tps);
 	/* If we executed a task, bump the stats */
 	if (tps->stats) {
 		tps->stats->_tasks_processed_count++;
diff --git a/main/tcptls.c b/main/tcptls.c
index 6a36aaf..34baf9a 100644
--- a/main/tcptls.c
+++ b/main/tcptls.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 425991 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #ifdef HAVE_FCNTL_H
 #include <fcntl.h>
@@ -400,7 +400,11 @@ static int tcptls_stream_close(void *cookie)
 
 			if (!stream->ssl->server) {
 				/* For client threads, ensure that the error stack is cleared */
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+				ERR_remove_thread_state(NULL);
+#else
 				ERR_remove_state(0);
+#endif	/* OPENSSL_VERSION_NUMBER >= 0x10000000L */
 			}
 
 			SSL_free(stream->ssl);
@@ -640,9 +644,15 @@ static void *handle_tcptls_connection(void *data)
 							break;
 						}
 						str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, pos));
-						ASN1_STRING_to_UTF8(&str2, str);
+						ret = ASN1_STRING_to_UTF8(&str2, str);
+						if (ret < 0) {
+							continue;
+						}
+
 						if (str2) {
-							if (!strcasecmp(tcptls_session->parent->hostname, (char *) str2)) {
+							if (strlen((char *) str2) != ret) {
+								ast_log(LOG_WARNING, "Invalid certificate common name length (contains NULL bytes?)\n");
+							} else if (!strcasecmp(tcptls_session->parent->hostname, (char *) str2)) {
 								found = 1;
 							}
 							ast_debug(3, "SSL Common Name compare s1='%s' s2='%s'\n", tcptls_session->parent->hostname, str2);
@@ -708,8 +718,9 @@ void *ast_tcptls_server_root(void *data)
 		}
 		fd = ast_accept(desc->accept_fd, &addr);
 		if (fd < 0) {
-			if ((errno != EAGAIN) && (errno != EINTR)) {
+			if ((errno != EAGAIN) && (errno != EWOULDBLOCK) && (errno != EINTR) && (errno != ECONNABORTED)) {
 				ast_log(LOG_ERROR, "Accept failed: %s\n", strerror(errno));
+				break;
 			}
 			continue;
 		}
@@ -748,7 +759,8 @@ static int __ssl_setup(struct ast_tls_config *cfg, int client)
 	return 0;
 #else
 	int disable_ssl = 0;
- 
+	long ssl_opts = 0;
+
 	if (!cfg->enabled) {
 		return 0;
 	}
@@ -768,10 +780,13 @@ static int __ssl_setup(struct ast_tls_config *cfg, int client)
 			cfg->ssl_ctx = SSL_CTX_new(SSLv2_client_method());
 		} else
 #endif
+#ifndef OPENSSL_NO_SSL3_METHOD
 		if (ast_test_flag(&cfg->flags, AST_SSL_SSLV3_CLIENT)) {
 			ast_log(LOG_WARNING, "Usage of SSLv3 is discouraged due to known vulnerabilities. Please use 'tlsv1' or leave the TLS method unspecified!\n");
 			cfg->ssl_ctx = SSL_CTX_new(SSLv3_client_method());
-		} else if (ast_test_flag(&cfg->flags, AST_SSL_TLSV1_CLIENT)) {
+		} else
+#endif
+		if (ast_test_flag(&cfg->flags, AST_SSL_TLSV1_CLIENT)) {
 			cfg->ssl_ctx = SSL_CTX_new(TLSv1_client_method());
 		} else {
 			disable_ssl = 1;
@@ -793,11 +808,29 @@ static int __ssl_setup(struct ast_tls_config *cfg, int client)
 	 * them. SSLv23_*_method supports TLSv1+.
 	 */
 	if (disable_ssl) {
-		long ssl_opts;
+		ssl_opts |= SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
+	}
+
+	if (ast_test_flag(&cfg->flags, AST_SSL_SERVER_CIPHER_ORDER)) {
+		ssl_opts |= SSL_OP_CIPHER_SERVER_PREFERENCE;
+	}
 
-		ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
-		SSL_CTX_set_options(cfg->ssl_ctx, ssl_opts);
+	if (ast_test_flag(&cfg->flags, AST_SSL_DISABLE_TLSV1)) {
+		ssl_opts |= SSL_OP_NO_TLSv1;
+	}
+#if defined(HAVE_SSL_OP_NO_TLSV1_1) && defined(HAVE_SSL_OP_NO_TLSV1_2)
+	if (ast_test_flag(&cfg->flags, AST_SSL_DISABLE_TLSV11)) {
+		ssl_opts |= SSL_OP_NO_TLSv1_1;
 	}
+	if (ast_test_flag(&cfg->flags, AST_SSL_DISABLE_TLSV12)) {
+		ssl_opts |= SSL_OP_NO_TLSv1_2;
+	}
+#else
+	ast_log(LOG_WARNING, "Your version of OpenSSL leaves you potentially vulnerable "
+			"to the SSL BEAST attack. Please upgrade to OpenSSL 1.0.1 or later\n");
+#endif
+
+	SSL_CTX_set_options(cfg->ssl_ctx, ssl_opts);
 
 	SSL_CTX_set_verify(cfg->ssl_ctx,
 		ast_test_flag(&cfg->flags, AST_SSL_VERIFY_CLIENT) ? SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT : SSL_VERIFY_NONE,
@@ -1150,6 +1183,14 @@ int ast_tls_read_conf(struct ast_tls_config *tls_cfg, struct ast_tcptls_session_
 			ast_clear_flag(&tls_cfg->flags, AST_SSL_TLSV1_CLIENT);
 			ast_clear_flag(&tls_cfg->flags, AST_SSL_SSLV3_CLIENT);
 		}
+	} else if (!strcasecmp(varname, "tlsservercipherorder")) {
+		ast_set2_flag(&tls_cfg->flags, ast_true(value), AST_SSL_SERVER_CIPHER_ORDER);
+	} else if (!strcasecmp(varname, "tlsdisablev1")) {
+		ast_set2_flag(&tls_cfg->flags, ast_true(value), AST_SSL_DISABLE_TLSV1);
+	} else if (!strcasecmp(varname, "tlsdisablev11")) {
+		ast_set2_flag(&tls_cfg->flags, ast_true(value), AST_SSL_DISABLE_TLSV11);
+	} else if (!strcasecmp(varname, "tlsdisablev12")) {
+		ast_set2_flag(&tls_cfg->flags, ast_true(value), AST_SSL_DISABLE_TLSV12);
 	} else {
 		return -1;
 	}
diff --git a/main/tdd.c b/main/tdd.c
index 8bbeae0..a590e3b 100644
--- a/main/tdd.c
+++ b/main/tdd.c
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 373330 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <time.h>
 #include <math.h>
diff --git a/main/term.c b/main/term.c
index da7c2ef..6a1a645 100644
--- a/main/term.c
+++ b/main/term.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 381448 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/_private.h"
 #include <sys/time.h>
@@ -175,16 +175,14 @@ end:
 		if (ast_opt_light_background) {
 			snprintf(prepdata, sizeof(prepdata), "%c[%dm", ESC, COLOR_BROWN);
 			snprintf(enddata, sizeof(enddata), "%c[%dm", ESC, COLOR_BLACK);
-			snprintf(quitdata, sizeof(quitdata), "%c[0m", ESC);
 		} else if (ast_opt_force_black_background) {
 			snprintf(prepdata, sizeof(prepdata), "%c[%d;%d;%dm", ESC, ATTR_BRIGHT, COLOR_BROWN, COLOR_BLACK + 10);
 			snprintf(enddata, sizeof(enddata), "%c[%d;%d;%dm", ESC, ATTR_RESET, COLOR_WHITE, COLOR_BLACK + 10);
-			snprintf(quitdata, sizeof(quitdata), "%c[0m", ESC);
 		} else {
 			snprintf(prepdata, sizeof(prepdata), "%c[%d;%dm", ESC, ATTR_BRIGHT, COLOR_BROWN);
-			snprintf(enddata, sizeof(enddata), "%c[%d;%dm", ESC, ATTR_RESET, COLOR_WHITE);
-			snprintf(quitdata, sizeof(quitdata), "%c[0m", ESC);
+			snprintf(enddata, sizeof(enddata), "%c[%dm", ESC, ATTR_RESET);
 		}
+		snprintf(quitdata, sizeof(quitdata), "%c[%dm", ESC, ATTR_RESET);
 	}
 	return 0;
 }
@@ -216,9 +214,12 @@ char *term_color(char *outbuf, const char *inbuf, int fgcolor, int bgcolor, int
 	}
 
 	if (ast_opt_force_black_background) {
-		snprintf(outbuf, maxout, "%c[%d;%d;%dm%s%c[%d;%dm", ESC, attr, fgcolor, bgcolor + 10, inbuf, ESC, COLOR_WHITE, COLOR_BLACK + 10);
+		if (!bgcolor) {
+			bgcolor = COLOR_BLACK;
+		}
+		snprintf(outbuf, maxout, "%c[%d;%d;%dm%s%s", ESC, attr, fgcolor, bgcolor + 10, inbuf, term_end());
 	} else {
-		snprintf(outbuf, maxout, "%c[%d;%dm%s%c[0m", ESC, attr, fgcolor, inbuf, ESC);
+		snprintf(outbuf, maxout, "%c[%d;%dm%s%s", ESC, attr, fgcolor, inbuf, term_end());
 	}
 	return outbuf;
 }
@@ -242,16 +243,16 @@ static void check_bgcolor(int *bgcolor)
 	}
 }
 
-static int check_colors_allowed(int fgcolor)
+static int check_colors_allowed(void)
 {
-	return (!vt100compat || !fgcolor) ? 0 : 1;
+	return vt100compat;
 }
 
 int ast_term_color_code(struct ast_str **str, int fgcolor, int bgcolor)
 {
 	int attr = 0;
 
-	if (!check_colors_allowed(fgcolor)) {
+	if (!check_colors_allowed()) {
 		return -1;
 	}
 
@@ -273,7 +274,7 @@ char *term_color_code(char *outbuf, int fgcolor, int bgcolor, int maxout)
 {
 	int attr = 0;
 
-	if (!check_colors_allowed(fgcolor)) {
+	if (!check_colors_allowed()) {
 		*outbuf = '\0';
 		return outbuf;
 	}
@@ -310,12 +311,7 @@ const char *ast_term_color(int fgcolor, int bgcolor)
 
 const char *ast_term_reset(void)
 {
-	if (ast_opt_force_black_background) {
-		static const char reset[] = { ESC, '[', COLOR_BLACK + 10, 'm', 0 };
-		return reset;
-	} else {
-		return quitdata;
-	}
+	return term_end();
 }
 
 char *term_strip(char *outbuf, const char *inbuf, int maxout)
diff --git a/main/test.c b/main/test.c
index 7f49aea..e9b56eb 100644
--- a/main/test.c
+++ b/main/test.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428973 $");
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
 
 #include "asterisk/_private.h"
 
@@ -110,6 +110,7 @@ static struct ast_test *test_free(struct ast_test *test);
 static int test_insert(struct ast_test *test);
 static struct ast_test *test_remove(ast_test_cb_t *cb);
 static int test_cat_cmp(const char *cat1, const char *cat2);
+static int registration_errors = 0;
 
 void ast_test_debug(struct ast_test *test, const char *fmt, ...)
 {
@@ -197,16 +198,19 @@ int ast_test_register(ast_test_cb_t *cb)
 	struct ast_test *test;
 
 	if (!cb) {
-		ast_log(LOG_WARNING, "Attempted to register test without all required information\n");
+		ast_log(LOG_ERROR, "Attempted to register test without all required information\n");
+		registration_errors++;
 		return -1;
 	}
 
 	if (!(test = test_alloc(cb))) {
+		registration_errors++;
 		return -1;
 	}
 
 	if (test_insert(test)) {
 		test_free(test);
+		registration_errors++;
 		return -1;
 	}
 
@@ -619,7 +623,9 @@ static struct ast_test *test_alloc(ast_test_cb_t *cb)
 {
 	struct ast_test *test;
 
-	if (!cb || !(test = ast_calloc(1, sizeof(*test)))) {
+	test = ast_calloc(1, sizeof(*test));
+	if (!test) {
+		ast_log(LOG_ERROR, "Failed to allocate test, registration failed.\n");
 		return NULL;
 	}
 
@@ -628,34 +634,59 @@ static struct ast_test *test_alloc(ast_test_cb_t *cb)
 	test->cb(&test->info, TEST_INIT, test);
 
 	if (ast_strlen_zero(test->info.name)) {
-		ast_log(LOG_WARNING, "Test has no name, test registration refused.\n");
+		ast_log(LOG_ERROR, "Test has no name, test registration refused.\n");
 		return test_free(test);
 	}
 
 	if (ast_strlen_zero(test->info.category)) {
-		ast_log(LOG_WARNING, "Test %s has no category, test registration refused.\n",
-				test->info.name);
+		ast_log(LOG_ERROR, "Test %s has no category, test registration refused.\n",
+			test->info.name);
 		return test_free(test);
 	}
 
 	if (test->info.category[0] != '/' || test->info.category[strlen(test->info.category) - 1] != '/') {
 		ast_log(LOG_WARNING, "Test category '%s' for test '%s' is missing a leading or trailing slash.\n",
-				test->info.category, test->info.name);
+			test->info.category, test->info.name);
+		/*
+		 * Flag an error anyways so test_registrations fails but allow the
+		 * test to be registered.
+		 */
+		++registration_errors;
 	}
 
 	if (ast_strlen_zero(test->info.summary)) {
-		ast_log(LOG_WARNING, "Test %s%s has no summary, test registration refused.\n",
-				test->info.category, test->info.name);
+		ast_log(LOG_ERROR, "Test %s%s has no summary, test registration refused.\n",
+			test->info.category, test->info.name);
 		return test_free(test);
 	}
+	if (test->info.summary[strlen(test->info.summary) - 1] == '\n') {
+		ast_log(LOG_WARNING, "Test %s%s summary has a trailing newline.\n",
+			test->info.category, test->info.name);
+		/*
+		 * Flag an error anyways so test_registrations fails but allow the
+		 * test to be registered.
+		 */
+		++registration_errors;
+	}
 
 	if (ast_strlen_zero(test->info.description)) {
-		ast_log(LOG_WARNING, "Test %s%s has no description, test registration refused.\n",
-				test->info.category, test->info.name);
+		ast_log(LOG_ERROR, "Test %s%s has no description, test registration refused.\n",
+			test->info.category, test->info.name);
 		return test_free(test);
 	}
+	if (test->info.description[strlen(test->info.description) - 1] == '\n') {
+		ast_log(LOG_WARNING, "Test %s%s description has a trailing newline.\n",
+			test->info.category, test->info.name);
+		/*
+		 * Flag an error anyways so test_registrations fails but allow the
+		 * test to be registered.
+		 */
+		++registration_errors;
+	}
 
 	if (!(test->status_str = ast_str_create(128))) {
+		ast_log(LOG_ERROR, "Failed to allocate status_str for %s%s, test registration failed.\n",
+			test->info.category, test->info.name);
 		return test_free(test);
 	}
 
@@ -1095,19 +1126,38 @@ void __ast_test_suite_event_notify(const char *file, const char *func, int line,
 	stasis_publish(ast_test_suite_topic(), msg);
 }
 
-#endif /* TEST_FRAMEWORK */
+AST_TEST_DEFINE(test_registrations)
+{
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "registrations";
+		info->category = "/main/test/";
+		info->summary = "Validate Test Registration Data.";
+		info->description = "Validate Test Registration Data.";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
 
-#ifdef TEST_FRAMEWORK
+	if (registration_errors) {
+		ast_test_status_update(test,
+			"%d test registration error%s occurred.  See startup logs for details.\n",
+			registration_errors, registration_errors > 1 ? "s" : "");
+		return AST_TEST_FAIL;
+	}
+
+	return AST_TEST_PASS;
+}
 
 static void test_cleanup(void)
 {
+	AST_TEST_UNREGISTER(test_registrations);
 	ast_cli_unregister_multiple(test_cli, ARRAY_LEN(test_cli));
 	ao2_cleanup(test_suite_topic);
 	test_suite_topic = NULL;
 	STASIS_MESSAGE_TYPE_CLEANUP(ast_test_suite_message_type);
 }
-
-#endif
+#endif /* TEST_FRAMEWORK */
 
 int ast_test_init(void)
 {
@@ -1124,6 +1174,8 @@ int ast_test_init(void)
 		return -1;
 	}
 
+	AST_TEST_REGISTER(test_registrations);
+
 	/* Register cli commands */
 	ast_cli_register_multiple(test_cli, ARRAY_LEN(test_cli));
 #endif
diff --git a/main/threadpool.c b/main/threadpool.c
index 597e83e..60e1e9a 100644
--- a/main/threadpool.c
+++ b/main/threadpool.c
@@ -168,7 +168,7 @@ static void *worker_start(void *arg);
 static struct worker_thread *worker_thread_alloc(struct ast_threadpool *pool);
 static int worker_thread_start(struct worker_thread *worker);
 static int worker_idle(struct worker_thread *worker);
-static void worker_set_state(struct worker_thread *worker, enum worker_state state);
+static int worker_set_state(struct worker_thread *worker, enum worker_state state);
 static void worker_shutdown(struct worker_thread *worker);
 
 /*!
@@ -482,7 +482,16 @@ static int activate_thread(void *obj, void *arg, int flags)
 				worker->id);
 		return 0;
 	}
-	worker_set_state(worker, ALIVE);
+
+	if (worker_set_state(worker, ALIVE)) {
+		ast_debug(1, "Failed to activate thread %d. It is dead\n",
+				worker->id);
+		/* The worker thread will no longer exist in the active threads or
+		 * idle threads container after this.
+		 */
+		ao2_unlink(pool->active_threads, worker);
+	}
+
 	return CMP_MATCH;
 }
 
@@ -538,20 +547,33 @@ static int queued_task_pushed(void *data)
 	struct task_pushed_data *tpd = data;
 	struct ast_threadpool *pool = tpd->pool;
 	int was_empty = tpd->was_empty;
+	unsigned int existing_active;
 
 	if (pool->listener && pool->listener->callbacks->task_pushed) {
 		pool->listener->callbacks->task_pushed(pool, pool->listener, was_empty);
 	}
-	if (ao2_container_count(pool->idle_threads) == 0) {
+
+	existing_active = ao2_container_count(pool->active_threads);
+
+	/* The first pass transitions any existing idle threads to be active, and
+	 * will also remove any worker threads that have recently entered the dead
+	 * state.
+	 */
+	ao2_callback(pool->idle_threads, OBJ_UNLINK | OBJ_NOLOCK | OBJ_NODATA,
+			activate_thread, pool);
+
+	/* If no idle threads could be transitioned to active grow the pool as permitted. */
+	if (ao2_container_count(pool->active_threads) == existing_active) {
 		if (!pool->options.auto_increment) {
+			ao2_ref(tpd, -1);
 			return 0;
 		}
 		grow(pool, pool->options.auto_increment);
+		/* An optional second pass transitions any newly added threads. */
+		ao2_callback(pool->idle_threads, OBJ_UNLINK | OBJ_NOLOCK | OBJ_NODATA,
+				activate_thread, pool);
 	}
 
-	ao2_callback(pool->idle_threads, OBJ_UNLINK | OBJ_NOLOCK | OBJ_NODATA,
-			activate_thread, pool);
-
 	threadpool_send_state_changed(pool);
 	ao2_ref(tpd, -1);
 	return 0;
@@ -797,7 +819,7 @@ static int queued_set_size(void *data)
 
 	/* We don't count zombie threads as being "live" when potentially resizing */
 	unsigned int current_size = ao2_container_count(pool->active_threads) +
-		ao2_container_count(pool->idle_threads);
+			ao2_container_count(pool->idle_threads);
 
 	if (current_size == num_threads) {
 		ast_debug(3, "Not changing threadpool size since new size %u is the same as current %u\n",
@@ -806,6 +828,12 @@ static int queued_set_size(void *data)
 	}
 
 	if (current_size < num_threads) {
+		ao2_callback(pool->idle_threads, OBJ_UNLINK | OBJ_NOLOCK | OBJ_NODATA | OBJ_MULTIPLE,
+				activate_thread, pool);
+
+		/* As the above may have altered the number of current threads update it */
+		current_size = ao2_container_count(pool->active_threads) +
+				ao2_container_count(pool->idle_threads);
 		grow(pool, num_threads - current_size);
 		ao2_callback(pool->idle_threads, OBJ_UNLINK | OBJ_NOLOCK | OBJ_NODATA | OBJ_MULTIPLE,
 				activate_thread, pool);
@@ -1117,27 +1145,158 @@ static int worker_idle(struct worker_thread *worker)
  *
  * The threadpool calls into this function in order to let a worker know
  * how it should proceed.
+ *
+ * \retval -1 failure (state transition not permitted)
+ * \retval 0 success
  */
-static void worker_set_state(struct worker_thread *worker, enum worker_state state)
+static int worker_set_state(struct worker_thread *worker, enum worker_state state)
 {
 	SCOPED_MUTEX(lock, &worker->lock);
+
+	switch (state) {
+	case ALIVE:
+		/* This can occur due to a race condition between being told to go active
+		 * and an idle timeout happening.
+		 */
+		if (worker->state == DEAD) {
+			return -1;
+		}
+		ast_assert(worker->state != ZOMBIE);
+		break;
+	case DEAD:
+		break;
+	case ZOMBIE:
+		ast_assert(worker->state != DEAD);
+		break;
+	}
+
 	worker->state = state;
 	worker->wake_up = 1;
 	ast_cond_signal(&worker->cond);
+
+	return 0;
+}
+
+/*! Serializer group shutdown control object. */
+struct ast_serializer_shutdown_group {
+	/*! Shutdown thread waits on this conditional. */
+	ast_cond_t cond;
+	/*! Count of serializers needing to shutdown. */
+	int count;
+};
+
+static void serializer_shutdown_group_dtor(void *vdoomed)
+{
+	struct ast_serializer_shutdown_group *doomed = vdoomed;
+
+	ast_cond_destroy(&doomed->cond);
+}
+
+struct ast_serializer_shutdown_group *ast_serializer_shutdown_group_alloc(void)
+{
+	struct ast_serializer_shutdown_group *shutdown_group;
+
+	shutdown_group = ao2_alloc(sizeof(*shutdown_group), serializer_shutdown_group_dtor);
+	if (!shutdown_group) {
+		return NULL;
+	}
+	ast_cond_init(&shutdown_group->cond, NULL);
+	return shutdown_group;
+}
+
+int ast_serializer_shutdown_group_join(struct ast_serializer_shutdown_group *shutdown_group, int timeout)
+{
+	int remaining;
+	ast_mutex_t *lock;
+
+	if (!shutdown_group) {
+		return 0;
+	}
+
+	lock = ao2_object_get_lockaddr(shutdown_group);
+	ast_assert(lock != NULL);
+
+	ao2_lock(shutdown_group);
+	if (timeout) {
+		struct timeval start;
+		struct timespec end;
+
+		start = ast_tvnow();
+		end.tv_sec = start.tv_sec + timeout;
+		end.tv_nsec = start.tv_usec * 1000;
+		while (shutdown_group->count) {
+			if (ast_cond_timedwait(&shutdown_group->cond, lock, &end)) {
+				/* Error or timed out waiting for the count to reach zero. */
+				break;
+			}
+		}
+	} else {
+		while (shutdown_group->count) {
+			if (ast_cond_wait(&shutdown_group->cond, lock)) {
+				/* Error */
+				break;
+			}
+		}
+	}
+	remaining = shutdown_group->count;
+	ao2_unlock(shutdown_group);
+	return remaining;
+}
+
+/*!
+ * \internal
+ * \brief Increment the number of serializer members in the group.
+ * \since 13.5.0
+ *
+ * \param shutdown_group Group shutdown controller.
+ *
+ * \return Nothing
+ */
+static void serializer_shutdown_group_inc(struct ast_serializer_shutdown_group *shutdown_group)
+{
+	ao2_lock(shutdown_group);
+	++shutdown_group->count;
+	ao2_unlock(shutdown_group);
+}
+
+/*!
+ * \internal
+ * \brief Decrement the number of serializer members in the group.
+ * \since 13.5.0
+ *
+ * \param shutdown_group Group shutdown controller.
+ *
+ * \return Nothing
+ */
+static void serializer_shutdown_group_dec(struct ast_serializer_shutdown_group *shutdown_group)
+{
+	ao2_lock(shutdown_group);
+	--shutdown_group->count;
+	if (!shutdown_group->count) {
+		ast_cond_signal(&shutdown_group->cond);
+	}
+	ao2_unlock(shutdown_group);
 }
 
 struct serializer {
+	/*! Threadpool the serializer will use to process the jobs. */
 	struct ast_threadpool *pool;
+	/*! Which group will wait for this serializer to shutdown. */
+	struct ast_serializer_shutdown_group *shutdown_group;
 };
 
 static void serializer_dtor(void *obj)
 {
 	struct serializer *ser = obj;
+
 	ao2_cleanup(ser->pool);
 	ser->pool = NULL;
+	ao2_cleanup(ser->shutdown_group);
+	ser->shutdown_group = NULL;
 }
 
-static struct serializer *serializer_create(struct ast_threadpool *pool)
+static struct serializer *serializer_create(struct ast_threadpool *pool,
+	struct ast_serializer_shutdown_group *shutdown_group)
 {
 	struct serializer *ser;
 
@@ -1147,16 +1306,21 @@ static struct serializer *serializer_create(struct ast_threadpool *pool)
 	}
 	ao2_ref(pool, +1);
 	ser->pool = pool;
+	ser->shutdown_group = ao2_bump(shutdown_group);
 	return ser;
 }
 
+AST_THREADSTORAGE_RAW(current_serializer);
+
 static int execute_tasks(void *data)
 {
 	struct ast_taskprocessor *tps = data;
 
+	ast_threadstorage_set_ptr(&current_serializer, tps);
 	while (ast_taskprocessor_execute(tps)) {
 		/* No-op */
 	}
+	ast_threadstorage_set_ptr(&current_serializer, NULL);
 
 	ast_taskprocessor_unreference(tps);
 	return 0;
@@ -1183,6 +1347,10 @@ static int serializer_start(struct ast_taskprocessor_listener *listener)
 static void serializer_shutdown(struct ast_taskprocessor_listener *listener)
 {
 	struct serializer *ser = ast_taskprocessor_listener_get_user_data(listener);
+
+	if (ser->shutdown_group) {
+		serializer_shutdown_group_dec(ser->shutdown_group);
+	}
 	ao2_cleanup(ser);
 }
 
@@ -1192,27 +1360,45 @@ static struct ast_taskprocessor_listener_callbacks serializer_tps_listener_callb
 	.shutdown = serializer_shutdown,
 };
 
-struct ast_taskprocessor *ast_threadpool_serializer(const char *name, struct ast_threadpool *pool)
+struct ast_taskprocessor *ast_threadpool_serializer_get_current(void)
 {
-	RAII_VAR(struct serializer *, ser, NULL, ao2_cleanup);
-	RAII_VAR(struct ast_taskprocessor_listener *, listener, NULL, ao2_cleanup);
-	struct ast_taskprocessor *tps = NULL;
+	return ast_threadstorage_get_ptr(&current_serializer);
+}
 
-	ser = serializer_create(pool);
+struct ast_taskprocessor *ast_threadpool_serializer_group(const char *name,
+	struct ast_threadpool *pool, struct ast_serializer_shutdown_group *shutdown_group)
+{
+	struct serializer *ser;
+	struct ast_taskprocessor_listener *listener;
+	struct ast_taskprocessor *tps;
+
+	ser = serializer_create(pool, shutdown_group);
 	if (!ser) {
 		return NULL;
 	}
 
 	listener = ast_taskprocessor_listener_alloc(&serializer_tps_listener_callbacks, ser);
 	if (!listener) {
+		ao2_ref(ser, -1);
 		return NULL;
 	}
-	ser = NULL; /* ownership transferred to listener */
+	/* ser ref transferred to listener */
 
 	tps = ast_taskprocessor_create_with_listener(name, listener);
-	if (!tps) {
-		return NULL;
+	if (tps && shutdown_group) {
+		serializer_shutdown_group_inc(shutdown_group);
 	}
 
+	ao2_ref(listener, -1);
 	return tps;
 }
+
+struct ast_taskprocessor *ast_threadpool_serializer(const char *name, struct ast_threadpool *pool)
+{
+	return ast_threadpool_serializer_group(name, pool, NULL);
+}
+
+long ast_threadpool_queue_size(struct ast_threadpool *pool)
+{
+	return ast_taskprocessor_size(pool->tps);
+}
diff --git a/main/threadstorage.c b/main/threadstorage.c
index 8515719..4c46a0f 100644
--- a/main/threadstorage.c
+++ b/main/threadstorage.c
@@ -38,7 +38,7 @@ void threadstorage_init(void)
 
 #else /* !defined(DEBUG_THREADLOCALS) */
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 397110 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/strings.h"
 #include "asterisk/utils.h"
@@ -258,7 +258,7 @@ void threadstorage_init(void)
 {
 	pthread_mutex_init(&threadstoragelock, NULL);
 	ast_cli_register_multiple(cli, ARRAY_LEN(cli));
-	ast_register_atexit(threadstorage_shutdown);
+	ast_register_cleanup(threadstorage_shutdown);
 }
 
 #endif /* !defined(DEBUG_THREADLOCALS) */
diff --git a/main/timing.c b/main/timing.c
index 67bcd14..ae8c3eb 100644
--- a/main/timing.c
+++ b/main/timing.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 407749 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/_private.h"
 
@@ -289,7 +289,7 @@ int ast_timing_init(void)
 		return -1;
 	}
 
-	ast_register_atexit(timing_shutdown);
+	ast_register_cleanup(timing_shutdown);
 
 	return ast_cli_register_multiple(cli_timing, ARRAY_LEN(cli_timing));
 }
diff --git a/main/translate.c b/main/translate.c
index 520793f..6a39bac 100644
--- a/main/translate.c
+++ b/main/translate.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 426079 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/time.h>
 #include <sys/resource.h>
@@ -44,6 +44,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 426079 $")
 #include "asterisk/cli.h"
 #include "asterisk/term.h"
 #include "asterisk/format.h"
+#include "asterisk/linkedlists.h"
 
 /*! \todo
  * TODO: sample frames for each supported input format.
@@ -297,6 +298,10 @@ static void destroy(struct ast_trans_pvt *pvt)
 		t->destroy(pvt);
 	}
 	ao2_cleanup(pvt->f.subclass.format);
+	if (pvt->explicit_dst) {
+		ao2_ref(pvt->explicit_dst, -1);
+		pvt->explicit_dst = NULL;
+	}
 	ast_free(pvt);
 	ast_module_unref(t->module);
 }
@@ -305,7 +310,7 @@ static void destroy(struct ast_trans_pvt *pvt)
  * \brief Allocate the descriptor, required outbuf space,
  * and possibly desc.
  */
-static struct ast_trans_pvt *newpvt(struct ast_translator *t)
+static struct ast_trans_pvt *newpvt(struct ast_translator *t, struct ast_format *explicit_dst)
 {
 	struct ast_trans_pvt *pvt;
 	int len;
@@ -331,6 +336,12 @@ static struct ast_trans_pvt *newpvt(struct ast_translator *t)
 	if (t->buf_size) {/* finally buffer and header */
 		pvt->outbuf.c = ofs + AST_FRIENDLY_OFFSET;
 	}
+	/*
+	 * If the format has an attribute module, explicit_dst includes the (joined)
+	 * result of the SDP negotiation. For example with the Opus Codec, the format
+	 * knows whether both parties want to do forward-error correction (FEC).
+	 */
+	pvt->explicit_dst = ao2_bump(explicit_dst);
 
 	ast_module_ref(t->module);
 
@@ -348,9 +359,16 @@ static struct ast_trans_pvt *newpvt(struct ast_translator *t)
 	pvt->f.src = pvt->t->name;
 	pvt->f.data.ptr = pvt->outbuf.c;
 
-	/* if the translator has not provided a format find one in the cache or create one */
+	/*
+	 * If the translator has not provided a format
+	 * A) use the joined one,
+	 * B) use the cached one, or
+	 * C) create one.
+	 */
 	if (!pvt->f.subclass.format) {
-		if (!ast_strlen_zero(pvt->t->format)) {
+		pvt->f.subclass.format = ao2_bump(pvt->explicit_dst);
+
+		if (!pvt->f.subclass.format && !ast_strlen_zero(pvt->t->format)) {
 			pvt->f.subclass.format = ast_format_cache_get(pvt->t->format);
 		}
 
@@ -379,9 +397,6 @@ static struct ast_trans_pvt *newpvt(struct ast_translator *t)
 /*! \brief framein wrapper, deals with bound checks.  */
 static int framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
 {
-	int ret;
-	int samples = pvt->samples;	/* initial value */
-
 	/* Copy the last in jb timing info to the pvt */
 	ast_copy_flags(&pvt->f, f, AST_FRFLAG_HAS_TIMING_INFO);
 	pvt->f.ts = f->ts;
@@ -405,12 +420,7 @@ static int framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
 	/* we require a framein routine, wouldn't know how to do
 	 * it otherwise.
 	 */
-	ret = pvt->t->framein(pvt, f);
-	/* diagnostic ... */
-	if (pvt->samples == samples)
-		ast_log(LOG_WARNING, "%s did not update samples %d\n",
-			pvt->t->name, pvt->samples);
-	return ret;
+	return pvt->t->framein(pvt, f);
 }
 
 /*! \brief generic frameout routine.
@@ -476,6 +486,7 @@ struct ast_trans_pvt *ast_translator_build_path(struct ast_format *dst, struct a
 
 	while (src_index != dst_index) {
 		struct ast_trans_pvt *cur;
+		struct ast_format *explicit_dst = NULL;
 		struct ast_translator *t = matrix_get(src_index, dst_index)->step;
 		if (!t) {
 			ast_log(LOG_WARNING, "No translator path from %s to %s\n",
@@ -483,7 +494,10 @@ struct ast_trans_pvt *ast_translator_build_path(struct ast_format *dst, struct a
 			AST_RWLIST_UNLOCK(&translators);
 			return NULL;
 		}
-		if (!(cur = newpvt(t))) {
+		if ((t->dst_codec.sample_rate == ast_format_get_sample_rate(dst)) && (t->dst_codec.type == ast_format_get_type(dst)) && (!strcmp(t->dst_codec.name, ast_format_get_name(dst)))) {
+			explicit_dst = dst;
+		}
+		if (!(cur = newpvt(t, explicit_dst))) {
 			ast_log(LOG_WARNING, "Failed to build translator step from %s to %s\n",
 				ast_format_get_name(src), ast_format_get_name(dst));
 			if (head) {
@@ -547,7 +561,12 @@ struct ast_frame *ast_translate(struct ast_trans_pvt *path, struct ast_frame *f,
 	}
 	delivery = f->delivery;
 	for (out = f; out && p ; p = p->next) {
-		framein(p, out);
+		struct ast_frame *current = out;
+
+		do {
+			framein(p, current);
+			current = AST_LIST_NEXT(current, frame_list);
+		} while (current);
 		if (out != f) {
 			ast_frfree(out);
 		}
@@ -556,22 +575,33 @@ struct ast_frame *ast_translate(struct ast_trans_pvt *path, struct ast_frame *f,
 	if (out) {
 		/* we have a frame, play with times */
 		if (!ast_tvzero(delivery)) {
-			/* Regenerate prediction after a discontinuity */
-			if (ast_tvzero(path->nextout)) {
-				path->nextout = ast_tvnow();
-			}
+			struct ast_frame *current = out;
 
-			/* Use next predicted outgoing timestamp */
-			out->delivery = path->nextout;
+			do {
+				/* Regenerate prediction after a discontinuity */
+				if (ast_tvzero(path->nextout)) {
+					path->nextout = ast_tvnow();
+				}
 
-			/* Predict next outgoing timestamp from samples in this
-			   frame. */
-			path->nextout = ast_tvadd(path->nextout, ast_samp2tv(
-				 out->samples, ast_format_get_sample_rate(out->subclass.format)));
-			if (f->samples != out->samples && ast_test_flag(out, AST_FRFLAG_HAS_TIMING_INFO)) {
-				ast_debug(4, "Sample size different %d vs %d\n", f->samples, out->samples);
-				ast_clear_flag(out, AST_FRFLAG_HAS_TIMING_INFO);
-			}
+				/* Use next predicted outgoing timestamp */
+				current->delivery = path->nextout;
+
+				/* Invalidate prediction if we're entering a silence period */
+				if (current->frametype == AST_FRAME_CNG) {
+					path->nextout = ast_tv(0, 0);
+				/* Predict next outgoing timestamp from samples in this
+				   frame. */
+				} else {
+					path->nextout = ast_tvadd(path->nextout, ast_samp2tv(
+						current->samples, ast_format_get_sample_rate(current->subclass.format)));
+				}
+
+				if (f->samples != current->samples && ast_test_flag(current, AST_FRFLAG_HAS_TIMING_INFO)) {
+					ast_debug(4, "Sample size different %d vs %d\n", f->samples, current->samples);
+					ast_clear_flag(current, AST_FRFLAG_HAS_TIMING_INFO);
+				}
+				current = AST_LIST_NEXT(current, frame_list);
+			} while (current);
 		} else {
 			out->delivery = ast_tv(0, 0);
 			ast_set2_flag(out, has_timing_info, AST_FRFLAG_HAS_TIMING_INFO);
@@ -580,10 +610,10 @@ struct ast_frame *ast_translate(struct ast_trans_pvt *path, struct ast_frame *f,
 				out->len = len;
 				out->seqno = seqno;
 			}
-		}
-		/* Invalidate prediction if we're entering a silence period */
-		if (out->frametype == AST_FRAME_CNG) {
-			path->nextout = ast_tv(0, 0);
+			/* Invalidate prediction if we're entering a silence period */
+			if (out->frametype == AST_FRAME_CNG) {
+				path->nextout = ast_tv(0, 0);
+			}
 		}
 	}
 	if (consume) {
@@ -621,7 +651,7 @@ static void generate_computational_cost(struct ast_translator *t, int seconds)
 		return;
 	}
 
-	pvt = newpvt(t);
+	pvt = newpvt(t, NULL);
 	if (!pvt) {
 		ast_log(LOG_WARNING, "Translator '%s' appears to be broken and will probably fail.\n", t->name);
 		t->comp_cost = 999999;
@@ -1271,9 +1301,12 @@ int ast_translator_best_choice(struct ast_format_cap *dst_cap,
 {
 	unsigned int besttablecost = INT_MAX;
 	unsigned int beststeps = INT_MAX;
+	struct ast_format *fmt;
+	struct ast_format *dst;
+	struct ast_format *src;
 	RAII_VAR(struct ast_format *, best, NULL, ao2_cleanup);
 	RAII_VAR(struct ast_format *, bestdst, NULL, ao2_cleanup);
-	RAII_VAR(struct ast_format_cap *, joint_cap, NULL, ao2_cleanup);
+	struct ast_format_cap *joint_cap;
 	int i;
 	int j;
 
@@ -1282,80 +1315,71 @@ int ast_translator_best_choice(struct ast_format_cap *dst_cap,
 		return -1;
 	}
 
-	if (!(joint_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {
+	joint_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
+	if (!joint_cap) {
 		return -1;
 	}
 	ast_format_cap_get_compatible(dst_cap, src_cap, joint_cap);
 
-	for (i = 0; i < ast_format_cap_count(joint_cap); ++i) {
-		struct ast_format *fmt =
-			ast_format_cap_get_format(joint_cap, i);
-
-		if (!fmt) {
-			continue;
-		}
-
-		if (!best) {
-			/* No ao2_ref operations needed, we're done with fmt */
-			best = fmt;
+	for (i = 0; i < ast_format_cap_count(joint_cap); ++i, ao2_cleanup(fmt)) {
+		fmt = ast_format_cap_get_format(joint_cap, i);
+		if (!fmt
+			|| ast_format_get_type(fmt) != AST_MEDIA_TYPE_AUDIO) {
 			continue;
 		}
 
-		if (ast_format_get_sample_rate(best) <
-		    ast_format_get_sample_rate(fmt)) {
+		if (!best
+			|| ast_format_get_sample_rate(best) < ast_format_get_sample_rate(fmt)) {
 			ao2_replace(best, fmt);
 		}
-		ao2_ref(fmt, -1);
 	}
+	ao2_ref(joint_cap, -1);
 
 	if (best) {
 		ao2_replace(*dst_fmt_out, best);
 		ao2_replace(*src_fmt_out, best);
 		return 0;
 	}
+
 	/* need to translate */
 	AST_RWLIST_RDLOCK(&translators);
-
-	for (i = 0; i < ast_format_cap_count(dst_cap); ++i) {
-		struct ast_format *dst =
-			ast_format_cap_get_format(dst_cap, i);
-
-		if (!dst) {
+	for (i = 0; i < ast_format_cap_count(dst_cap); ++i, ao2_cleanup(dst)) {
+		dst = ast_format_cap_get_format(dst_cap, i);
+		if (!dst
+			|| ast_format_get_type(dst) != AST_MEDIA_TYPE_AUDIO) {
 			continue;
 		}
 
-		for (j = 0; j < ast_format_cap_count(src_cap); ++j) {
-			struct ast_format *src =
-				ast_format_cap_get_format(src_cap, j);
-			int x, y;
+		for (j = 0; j < ast_format_cap_count(src_cap); ++j, ao2_cleanup(src)) {
+			int x;
+			int y;
 
-			if (!src) {
+			src = ast_format_cap_get_format(src_cap, j);
+			if (!src
+				|| ast_format_get_type(src) != AST_MEDIA_TYPE_AUDIO) {
 				continue;
 			}
 
 			x = format2index(src);
 			y = format2index(dst);
 			if (x < 0 || y < 0) {
-				ao2_ref(src, -1);
 				continue;
 			}
 			if (!matrix_get(x, y) || !(matrix_get(x, y)->step)) {
-				ao2_ref(src, -1);
 				continue;
 			}
-			if (((matrix_get(x, y)->table_cost < besttablecost) ||
-			     (matrix_get(x, y)->multistep < beststeps))) {
+			if (matrix_get(x, y)->table_cost < besttablecost
+				|| matrix_get(x, y)->multistep < beststeps) {
 				/* better than what we have so far */
 				ao2_replace(best, src);
 				ao2_replace(bestdst, dst);
 				besttablecost = matrix_get(x, y)->table_cost;
 				beststeps = matrix_get(x, y)->multistep;
 			}
-			ao2_ref(src, -1);
 		}
-		ao2_ref(dst, -1);
 	}
 	AST_RWLIST_UNLOCK(&translators);
+
 	if (!best) {
 		return -1;
 	}
@@ -1484,7 +1508,9 @@ static void translate_shutdown(void)
 		ast_free(__matrix[x]);
 	}
 	ast_free(__matrix);
+	__matrix = NULL;
 	ast_free(__indextable);
+	__indextable = NULL;
 	ast_rwlock_unlock(&tablelock);
 	ast_rwlock_destroy(&tablelock);
 }
@@ -1495,6 +1521,6 @@ int ast_translate_init(void)
 	ast_rwlock_init(&tablelock);
 	res = matrix_resize(1);
 	res |= ast_cli_register_multiple(cli_translate, ARRAY_LEN(cli_translate));
-	ast_register_atexit(translate_shutdown);
+	ast_register_cleanup(translate_shutdown);
 	return res;
 }
diff --git a/main/udptl.c b/main/udptl.c
index c42e69b..e8410cc 100644
--- a/main/udptl.c
+++ b/main/udptl.c
@@ -63,7 +63,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 417327 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/time.h>
 #include <signal.h>
@@ -305,16 +305,15 @@ static int decode_open_type(uint8_t *buf, unsigned int limit, unsigned int *len,
 	if (decode_length(buf, limit, len, &octet_cnt) != 0)
 		return -1;
 
-	if (octet_cnt > 0) {
-		/* Make sure the buffer contains at least the number of bits requested */
-		if ((*len + octet_cnt) > limit)
-			return -1;
-
-		*p_num_octets = octet_cnt;
-		*p_object = &buf[*len];
-		*len += octet_cnt;
+	/* Make sure the buffer contains at least the number of bits requested */
+	if ((*len + octet_cnt) > limit) {
+		return -1;
 	}
 
+	*p_num_octets = octet_cnt;
+	*p_object = &buf[*len];
+	*len += octet_cnt;
+
 	return 0;
 }
 /*- End of function --------------------------------------------------------*/
@@ -362,8 +361,7 @@ static int encode_open_type(const struct ast_udptl *udptl, uint8_t *buf, unsigne
 	}
 	/* Encode the open type */
 	for (octet_idx = 0; ; num_octets -= enclen, octet_idx += enclen) {
-		if ((enclen = encode_length(buf, len, num_octets)) < 0)
-			return -1;
+		enclen = encode_length(buf, len, num_octets);
 		if (enclen + *len > buflen) {
 			ast_log(LOG_ERROR, "UDPTL (%s): Buffer overflow detected (%u + %u > %u)\n",
 				LOG_TAG(udptl), enclen, *len, buflen);
@@ -520,7 +518,7 @@ static int udptl_rx_packet(struct ast_udptl *s, uint8_t *buf, unsigned int len)
 #if 0
 			fprintf(stderr, "FEC: ");
 			for (j = 0; j < s->rx[x].fec_len[i]; j++)
-				fprintf(stderr, "%02X ", data[j]);
+				fprintf(stderr, "%02hhX ", data[j]);
 			fprintf(stderr, "\n");
 #endif
 		}
@@ -646,8 +644,7 @@ static int udptl_build_packet(struct ast_udptl *s, uint8_t *buf, unsigned int bu
 		buf[len++] = 0x00;
 		/* The number of entries will always be zero, so it is pointless allowing
 		   for the fragmented case here. */
-		if (encode_length(buf, &len, 0) < 0)
-			return -1;
+		encode_length(buf, &len, 0);
 		break;
 	case UDPTL_ERROR_CORRECTION_REDUNDANCY:
 		/* Encode the error recovery type */
@@ -658,8 +655,7 @@ static int udptl_build_packet(struct ast_udptl *s, uint8_t *buf, unsigned int bu
 			entries = s->tx_seq_no;
 		/* The number of entries will always be small, so it is pointless allowing
 		   for the fragmented case here. */
-		if (encode_length(buf, &len, entries) < 0)
-			return -1;
+		encode_length(buf, &len, entries);
 		/* Encode the elements */
 		for (i = 0; i < entries; i++) {
 			j = (entry - i - 1) & UDPTL_BUF_MASK;
@@ -1403,5 +1399,5 @@ void ast_udptl_init(void)
 
 	ast_cli_register_multiple(cli_udptl, ARRAY_LEN(cli_udptl));
 
-	ast_register_atexit(udptl_shutdown);
+	ast_register_cleanup(udptl_shutdown);
 }
diff --git a/main/ulaw.c b/main/ulaw.c
index abab853..76e4b04 100644
--- a/main/ulaw.c
+++ b/main/ulaw.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 369013 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/ulaw.h"
 #include "asterisk/logger.h"
diff --git a/main/utils.c b/main/utils.c
index d9b363d..3a8d46a 100644
--- a/main/utils.c
+++ b/main/utils.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 427384 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <ctype.h>
 #include <fcntl.h>
@@ -75,6 +75,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 427384 $")
 static char base64[64];
 static char b2a[256];
 
+/* This is for binary compatibility with modules built before
+ * ast_log_safe existed. */
+#define _AST_MEM_BACKTRACE_BUFLEN 60
+void *_ast_mem_backtrace_buffer[_AST_MEM_BACKTRACE_BUFLEN];
+
 AST_THREADSTORAGE(inet_ntoa_buf);
 
 #if !defined(HAVE_GETHOSTBYNAME_R_5) && !defined(HAVE_GETHOSTBYNAME_R_6)
@@ -251,7 +256,7 @@ void ast_md5_hash(char *output, const char *input)
 	MD5Final(digest, &md5);
 	ptr = output;
 	for (x = 0; x < 16; x++)
-		ptr += sprintf(ptr, "%2.2x", (unsigned)digest[x]);
+		ptr += sprintf(ptr, "%02hhx", digest[x]);
 }
 
 /*! \brief Produce 40 char SHA1 hash of value. */
@@ -269,7 +274,7 @@ void ast_sha1_hash(char *output, const char *input)
 	SHA1Result(&sha, Message_Digest);
 	ptr = output;
 	for (x = 0; x < 20; x++)
-		ptr += sprintf(ptr, "%2.2x", (unsigned)Message_Digest[x]);
+		ptr += sprintf(ptr, "%02hhx", Message_Digest[x]);
 }
 
 /*! \brief Produce a 20 byte SHA1 hash of value. */
@@ -420,7 +425,7 @@ char *ast_uri_encode(const char *string, char *outbuf, int buflen, struct ast_fl
 			if (out - outbuf >= buflen - 3) {
 				break;
 			}
-			out += sprintf(out, "%%%02X", (unsigned) *ptr);
+			out += sprintf(out, "%%%02hhX", (unsigned char) *ptr);
 		} else {
 			*out = *ptr;	/* Continue copying the string */
 			out++;
@@ -611,7 +616,7 @@ const char *ast_inet_ntoa(struct in_addr ia)
 	return inet_ntop(AF_INET, &ia, buf, INET_ADDRSTRLEN);
 }
 
-static int dev_urandom_fd;
+static int dev_urandom_fd = -1;
 
 #ifndef __linux__
 #undef pthread_create /* For ast_pthread_create function only */
@@ -1251,8 +1256,8 @@ int ast_pthread_create_stack(pthread_t *thread, pthread_attr_t *attr, void *(*st
 		pthread_attr_init(attr);
 	}
 
-#ifdef __linux__
-	/* On Linux, pthread_attr_init() defaults to PTHREAD_EXPLICIT_SCHED,
+#if defined(__linux__) || defined(__FreeBSD__)
+	/* On Linux and FreeBSD , pthread_attr_init() defaults to PTHREAD_EXPLICIT_SCHED,
 	   which is kind of useless. Change this here to
 	   PTHREAD_INHERIT_SCHED; that way the -p option to set realtime
 	   priority will propagate down to new threads by default.
@@ -1399,7 +1404,13 @@ int ast_carefulwrite(int fd, char *s, int len, int timeoutms)
 
 		if (res < 0 && errno != EAGAIN && errno != EINTR) {
 			/* fatal error from write() */
-			ast_log(LOG_ERROR, "write() returned error: %s\n", strerror(errno));
+			if (errno == EPIPE) {
+#ifndef STANDALONE
+				ast_debug(1, "write() failed due to reading end being closed: %s\n", strerror(errno));
+#endif
+			} else {
+				ast_log(LOG_ERROR, "write() returned error: %s\n", strerror(errno));
+			}
 			return -1;
 		}
 
@@ -1618,6 +1629,136 @@ char *ast_unescape_c(char *src)
 	return ret;
 }
 
+/*
+ * Standard escape sequences - Note, '\0' is not included as a valid character
+ * to escape, but instead is used here as a NULL terminator for the string.
+ */
+char escape_sequences[] = {
+	'\a', '\b', '\f', '\n', '\r', '\t', '\v', '\\', '\'', '\"', '\?', '\0'
+};
+
+/*
+ * Standard escape sequences output map (has to maintain matching order with
+ * escape_sequences). '\0' is included here as a NULL terminator for the string.
+ */
+static char escape_sequences_map[] = {
+	'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', '\'', '"', '?', '\0'
+};
+
+char *ast_escape(char *dest, const char *s, size_t size, const char *to_escape)
+{
+	char *p;
+	char *c;
+
+	if (!dest || !size) {
+		return dest;
+	}
+	if (ast_strlen_zero(s)) {
+		*dest = '\0';
+		return dest;
+	}
+
+	if (ast_strlen_zero(to_escape)) {
+		ast_copy_string(dest, s, size);
+		return dest;
+	}
+
+	for (p = dest; *s && --size; ++s, ++p) {
+		/* If in the list of characters to escape then escape it */
+		if (strchr(to_escape, *s)) {
+			if (!--size) {
+				/* Not enough room left for the escape sequence. */
+				break;
+			}
+
+			/*
+			 * See if the character to escape is part of the standard escape
+			 * sequences. If so we'll have to use its mapped counterpart
+			 * otherwise just use the current character.
+			 */
+			c = strchr(escape_sequences, *s);
+			*p++ = '\\';
+			*p = c ? escape_sequences_map[c - escape_sequences] : *s;
+		} else {
+			*p = *s;
+		}
+	}
+	*p = '\0';
+
+	return dest;
+}
+
+char *ast_escape_c(char *dest, const char *s, size_t size)
+{
+	/*
+	 * Note - This is an optimized version of ast_escape. When looking only
+	 * for escape_sequences a couple of checks used in the generic case can
+	 * be left out thus making it slightly more efficient.
+	 */
+	char *p;
+	char *c;
+
+	if (!dest || !size) {
+		return dest;
+	}
+	if (ast_strlen_zero(s)) {
+		*dest = '\0';
+		return dest;
+	}
+
+	for (p = dest; *s && --size; ++s, ++p) {
+		/*
+		 * See if the character to escape is part of the standard escape
+		 * sequences. If so use its mapped counterpart.
+		 */
+		c = strchr(escape_sequences, *s);
+		if (c) {
+			if (!--size) {
+				/* Not enough room left for the escape sequence. */
+				break;
+			}
+
+			*p++ = '\\';
+			*p = escape_sequences_map[c - escape_sequences];
+		} else {
+			*p = *s;
+		}
+	}
+	*p = '\0';
+
+	return dest;
+}
+
+static char *escape_alloc(const char *s, size_t *size)
+{
+	if (!s) {
+		return NULL;
+	}
+
+	/*
+	 * The result string needs to be twice the size of the given
+	 * string just in case every character in it needs to be escaped.
+	 */
+	*size = strlen(s) * 2 + 1;
+	return ast_malloc(*size);
+}
+
+char *ast_escape_alloc(const char *s, const char *to_escape)
+{
+	size_t size = 0;
+	char *dest = escape_alloc(s, &size);
+
+	return ast_escape(dest, s, size, to_escape);
+}
+
+char *ast_escape_c_alloc(const char *s)
+{
+	size_t size = 0;
+	char *dest = escape_alloc(s, &size);
+
+	return ast_escape_c(dest, s, size);
+}
+
 int ast_build_string_va(char **buffer, size_t *space, const char *fmt, va_list ap)
 {
 	int result;
@@ -1856,7 +1997,7 @@ void ast_join_delim(char *s, size_t len, const char * const w[], unsigned int si
 	/* Join words into a string */
 	if (!s)
 		return;
-	for (x = 0; ofs < len && w[x] && x < size; x++) {
+	for (x = 0; ofs < len && x < size && w[x] ; x++) {
 		if (x > 0)
 			s[ofs++] = delim;
 		for (src = w[x]; *src && ofs < len; src++)
@@ -2476,7 +2617,7 @@ int ast_utils_init(void)
 	ast_cli_register_multiple(utils_cli, ARRAY_LEN(utils_cli));
 #endif
 #endif
-	ast_register_atexit(utils_shutdown);
+	ast_register_cleanup(utils_shutdown);
 	return 0;
 }
 
@@ -2701,10 +2842,10 @@ char *ast_eid_to_str(char *s, int maxlen, struct ast_eid *eid)
 		}
 	} else {
 		for (x = 0; x < 5; x++) {
-			sprintf(s, "%02x:", (unsigned)eid->eid[x]);
+			sprintf(s, "%02hhx:", eid->eid[x]);
 			s += 3;
 		}
-		sprintf(s, "%02x", (unsigned)eid->eid[5]);
+		sprintf(s, "%02hhx", eid->eid[5]);
 	}
 	return os;
 }
@@ -2797,3 +2938,46 @@ int ast_eid_cmp(const struct ast_eid *eid1, const struct ast_eid *eid2)
 {
 	return memcmp(eid1, eid2, sizeof(*eid1));
 }
+
+int ast_file_is_readable(const char *filename)
+{
+#if defined(HAVE_EACCESS) || defined(HAVE_EUIDACCESS)
+#if defined(HAVE_EUIDACCESS) && !defined(HAVE_EACCESS)
+#define eaccess euidaccess
+#endif
+	return eaccess(filename, R_OK) == 0;
+#else
+	int fd = open(filename, O_RDONLY |  O_NONBLOCK);
+	if (fd < 0) {
+		return 0;
+	}
+	close(fd);
+	return 1;
+#endif
+}
+
+int ast_compare_versions(const char *version1, const char *version2)
+{
+	unsigned int major[2] = { 0 };
+	unsigned int minor[2] = { 0 };
+	unsigned int patch[2] = { 0 };
+	unsigned int extra[2] = { 0 };
+	int res;
+
+	sscanf(version1, "%u.%u.%u.%u", &major[0], &minor[0], &patch[0], &extra[0]);
+	sscanf(version2, "%u.%u.%u.%u", &major[1], &minor[1], &patch[1], &extra[1]);
+
+	res = major[0] - major[1];
+	if (res) {
+		return res;
+	}
+	res = minor[0] - minor[1];
+	if (res) {
+		return res;
+	}
+	res = patch[0] - patch[1];
+	if (res) {
+		return res;
+	}
+	return extra[0] - extra[1];
+}
diff --git a/main/uuid.c b/main/uuid.c
index ba864ac..3c5d7af 100644
--- a/main/uuid.c
+++ b/main/uuid.c
@@ -109,7 +109,7 @@ static void generate_uuid(struct ast_uuid *uuid)
 	 * or /dev/urandom not existing on systems in this age is next to none.
 	 */
 
-	/* XXX Currently, we only protect this call if the user has no /dev/urandon on their system.
+	/* XXX Currently, we only protect this call if the user has no /dev/urandom on their system.
 	 * If it turns out that there are issues with UUID generation despite the presence of
 	 * /dev/urandom, then we may need to make the locking/unlocking unconditional.
 	 */
diff --git a/main/xml.c b/main/xml.c
index 2a2bd8e..dee2497 100644
--- a/main/xml.c
+++ b/main/xml.c
@@ -31,7 +31,7 @@
 #include "asterisk/utils.h"
 #include "asterisk/autoconfig.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 400385 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #if defined(HAVE_LIBXML2)
 #include <libxml/parser.h>
diff --git a/main/xmldoc.c b/main/xmldoc.c
index d589a29..da753cd 100644
--- a/main/xmldoc.c
+++ b/main/xmldoc.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419822 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/_private.h"
 #include "asterisk/paths.h"
@@ -45,18 +45,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419822 $")
 /*! \brief Default documentation language. */
 static const char default_documentation_language[] = "en_US";
 
-/*!
- * \brief Number of columns to print when showing the XML documentation with a
- *         'core show application/function *' CLI command. Used in text wrapping.
- */
-static const int xmldoc_text_columns = 74;
-
-/*!
- * \brief This is a value that we will use to let the wrapping mechanism move the cursor
- *         backward and forward xmldoc_max_diff positions before cutting the middle of a
- *         word, trying to find a space or a \n.
- */
-static const int xmldoc_max_diff = 5;
+/*! \brief Number of columns to print when showing the XML documentation with a
+ *         'core show application/function *' CLI command. Used in text wrapping.*/
+static const int xmldoc_text_columns = 79;
 
 /*! \brief XML documentation language. */
 static char documentation_language[6];
@@ -176,100 +167,22 @@ static void xmldoc_setpostbr(char *postbr, size_t len, const char *text)
 
 /*!
  * \internal
- * \brief Try to find a space or a break in text starting at currentpost
- *         and moving at most maxdiff positions.
- *         Helper for xmldoc_string_wrap().
- *
- * \param text Input string where it will search.
- * \param currentpos Current position within text.
- * \param maxdiff Not move more than maxdiff inside text.
- *
- * \retval 1 if a space or break is found inside text while moving.
- * \retval 0 if no space or break is found.
- */
-static int xmldoc_wait_nextspace(const char *text, int currentpos, int maxdiff)
-{
-	int i, textlen;
-
-	if (!text) {
-		return 0;
-	}
-
-	textlen = strlen(text);
-	for (i = currentpos; i < textlen; i++) {
-		if (text[i] == ESC) {
-			/* Move to the end of the escape sequence */
-			while (i < textlen && text[i] != 'm') {
-				i++;
-			}
-		} else if (text[i] == ' ' || text[i] == '\n') {
-			/* Found the next space or linefeed */
-			return 1;
-		} else if (i - currentpos > maxdiff) {
-			/* We have looked the max distance and didn't find it */
-			return 0;
-		}
-	}
-
-	/* Reached the end and did not find it */
-
-	return 0;
-}
-
-/*!
- * \internal
- * \brief Helper function for xmldoc_string_wrap().
- *    Try to found a space or a break inside text moving backward
- *    not more than maxdiff positions.
- *
- * \param text The input string where to search for a space.
- * \param currentpos The current cursor position.
- * \param maxdiff The max number of positions to move within text.
- *
- * \retval 0 If no space is found (Notice that text[currentpos] is not a space or a break)
- * \retval > 0 If a space or a break is found, and the result is the position relative to
- *  currentpos.
- */
-static int xmldoc_foundspace_backward(const char *text, int currentpos, int maxdiff)
-{
-	int i;
-
-	for (i = currentpos; i > 0; i--) {
-		if (text[i] == ' ' || text[i] == '\n') {
-			return (currentpos - i);
-		} else if (text[i] == 'm' && (text[i - 1] >= '0' || text[i - 1] <= '9')) {
-			/* give up, we found the end of a possible ESC sequence. */
-			return 0;
-		} else if (currentpos - i > maxdiff) {
-			/* give up, we can't move anymore. */
-			return 0;
-		}
-	}
-
-	/* we found the beginning of the text */
-
-	return 0;
-}
-
-/*!
- * \internal
  * \brief Justify a text to a number of columns.
  *
  * \param text Input text to be justified.
  * \param columns Number of columns to preserve in the text.
- * \param maxdiff Try to not cut a word when goinf down.
  *
  * \retval NULL on error.
  * \retval The wrapped text.
  */
-static char *xmldoc_string_wrap(const char *text, int columns, int maxdiff)
+static char *xmldoc_string_wrap(const char *text, int columns)
 {
 	struct ast_str *tmp;
 	char *ret, postbr[160];
-	int count = 1, i, backspace, needtobreak = 0, colmax, textlen;
+	int count, i, textlen, postbrlen, lastbreak;
 
 	/* sanity check */
-	if (!text || columns <= 0 || maxdiff < 0) {
+	if (!text || columns <= 0) {
 		ast_log(LOG_WARNING, "Passing wrong arguments while trying to wrap the text\n");
 		return NULL;
 	}
@@ -282,55 +195,44 @@ static char *xmldoc_string_wrap(const char *text, int columns, int maxdiff)
 
 	/* Check for blanks and tabs and put them in postbr. */
 	xmldoc_setpostbr(postbr, sizeof(postbr), text);
-	colmax = columns - xmldoc_postbrlen(postbr);
+	postbrlen = xmldoc_postbrlen(postbr);
+
+	count = 0;
+	lastbreak = 0;
 
 	textlen = strlen(text);
 	for (i = 0; i < textlen; i++) {
-		if (needtobreak || !(count % colmax)) {
-			if (text[i] == ' ') {
-				ast_str_append(&tmp, 0, "\n%s", postbr);
-				needtobreak = 0;
-				count = 1;
-			} else if (text[i] != '\n') {
-				needtobreak = 1;
-				if (xmldoc_wait_nextspace(text, i, maxdiff)) {
-					/* wait for the next space */
-					ast_str_append(&tmp, 0, "%c", text[i]);
-					continue;
-				}
-				/* Try to look backwards */
-				backspace = xmldoc_foundspace_backward(text, i, maxdiff);
-				if (backspace) {
-					needtobreak = 1;
-					ast_str_truncate(tmp, -backspace);
-					i -= backspace + 1;
-					continue;
-				}
-				ast_str_append(&tmp, 0, "\n%s", postbr);
-				needtobreak = 0;
-				count = 1;
-			}
-			/* skip blanks after a \n */
-			while (text[i] == ' ') {
-				i++;
-			}
-		}
 		if (text[i] == '\n') {
 			xmldoc_setpostbr(postbr, sizeof(postbr), &text[i] + 1);
-			colmax = columns - xmldoc_postbrlen(postbr);
-			needtobreak = 0;
-			count = 1;
-		}
-		if (text[i] == ESC) {
-			/* Ignore Escape sequences. */
+			postbrlen = xmldoc_postbrlen(postbr);
+			count = 0;
+			lastbreak = 0;
+		} else if (text[i] == ESC) {
+			/* Walk over escape sequences without counting them. */
 			do {
 				ast_str_append(&tmp, 0, "%c", text[i]);
 				i++;
 			} while (i < textlen && text[i] != 'm');
 		} else {
+			if (text[i] == ' ') {
+				lastbreak = i;
+			}
 			count++;
 		}
-		ast_str_append(&tmp, 0, "%c", text[i]);
+
+		if (count > columns) {
+			/* Seek backwards if it was at most 30 characters ago. */
+			int back = i - lastbreak;
+			if (lastbreak && back > 0 && back < 30) {
+				ast_str_truncate(tmp, -back);
+				i = lastbreak; /* go back a bit */
+			}
+			ast_str_append(&tmp, 0, "\n%s", postbr);
+			count = postbrlen;
+			lastbreak = 0;
+		} else {
+			ast_str_append(&tmp, 0, "%c", text[i]);
+		}
 	}
 
 	ret = ast_strdup(ast_str_buffer(tmp));
@@ -442,7 +344,7 @@ char *ast_xmldoc_printable(const char *bwinput, int withcolors)
 	}
 
 	/* Wrap the text, notice that string wrap will avoid cutting an ESC sequence. */
-	wrapped = xmldoc_string_wrap(ast_str_buffer(colorized), xmldoc_text_columns, xmldoc_max_diff);
+	wrapped = xmldoc_string_wrap(ast_str_buffer(colorized), xmldoc_text_columns);
 
 	ast_free(colorized);
 
@@ -1239,7 +1141,7 @@ static char *xmldoc_get_syntax_config_option(struct ast_xml_node *fixnode, const
 	regex = ast_xml_get_attribute(fixnode, "regex");
 	ast_str_set(&syntax, 0, "%s = [%s] (Default: %s) (Regex: %s)\n",
 		name,
-		type,
+		type ?: "",
 		default_value ?: "n/a",
 		regex ?: "False");
 
@@ -2214,6 +2116,9 @@ static struct ast_str *xmldoc_get_formatted(struct ast_xml_node *node, int raw_o
 		ast_xml_free_text(tmpstr);
 	} else {
 		ret = ast_str_create(128);
+		if (!ret) {
+			return NULL;
+		}
 		for (tmp = ast_xml_node_get_children(node); tmp; tmp = ast_xml_node_get_next(tmp)) {
 			/* if found, parse children elements. */
 			if (xmldoc_parse_common_elements(tmp, "", "\n", &ret)) {
@@ -2263,7 +2168,7 @@ static char *_xmldoc_build_field(struct ast_xml_node *node, const char *var, int
 	}
 
 	formatted = xmldoc_get_formatted(node, raw, raw);
-	if (ast_str_strlen(formatted) > 0) {
+	if (formatted && ast_str_strlen(formatted) > 0) {
 		ret = ast_strdup(ast_str_buffer(formatted));
 	}
 	ast_free(formatted);
@@ -2533,7 +2438,8 @@ static struct ast_xml_doc_item *xmldoc_build_list_responses(struct ast_xml_node
 	/* Iterate over managerEvent nodes */
 	for (event = ast_xml_node_get_children(list_elements); event; event = ast_xml_node_get_next(event)) {
 		struct ast_xml_node *event_instance;
-		const char *name = ast_xml_get_attribute(event, "name");
+		RAII_VAR(const char *, name, ast_xml_get_attribute(event, "name"),
+			ast_xml_free_attr);
 		struct ast_xml_doc_item *new_item;
 
 		if (!name || strcmp(ast_xml_node_get_name(event), "managerEvent")) {
@@ -2607,10 +2513,16 @@ static struct ast_xml_doc_item *xmldoc_build_final_response(struct ast_xml_node
 		"managerEventInstance", NULL, NULL);
 	if (!event_instance) {
 		return NULL;
+	} else {
+		const char *name;
+		struct ast_xml_doc_item *res;
+
+		name = ast_xml_get_attribute(final_response_event, "name");
+		res = xmldoc_build_documentation_item(event_instance, name, "managerEvent");
+		ast_xml_free_attr(name);
+		return res;
 	}
 
-	return xmldoc_build_documentation_item(event_instance,
-		ast_xml_get_attribute(final_response_event, "name"), "managerEvent");
 }
 
 struct ast_xml_doc_item *ast_xmldoc_build_final_response(const char *type, const char *name, const char *module)
@@ -2636,14 +2548,18 @@ struct ast_xml_xpath_results *__attribute__((format(printf, 1, 2))) ast_xmldoc_q
 	struct documentation_tree *doctree;
 	RAII_VAR(struct ast_str *, xpath_str, ast_str_create(128), ast_free);
 	va_list ap;
+	int res;
 
 	if (!xpath_str) {
 		return NULL;
 	}
 
 	va_start(ap, fmt);
-	ast_str_set_va(&xpath_str, 0, fmt, ap);
+	res = ast_str_set_va(&xpath_str, 0, fmt, ap);
 	va_end(ap);
+	if (res == AST_DYNSTR_BUILD_FAILED) {
+		return NULL;
+	}
 
 	AST_RWLIST_RDLOCK(&xmldoc_tree);
 	AST_LIST_TRAVERSE(&xmldoc_tree, doctree, entry) {
@@ -2952,7 +2868,7 @@ int ast_xmldoc_load_documentation(void)
 
 	ast_cli_register(&cli_dump_xmldocs);
 	/* register function to be run when asterisk finish. */
-	ast_register_atexit(xmldoc_unload_documentation);
+	ast_register_cleanup(xmldoc_unload_documentation);
 
 	globbuf.gl_offs = 0;    /* slots to reserve in gl_pathv */
 
@@ -2969,7 +2885,7 @@ int ast_xmldoc_load_documentation(void)
 	globret = glob(xmlpattern, MY_GLOB_FLAGS, NULL, &globbuf);
 #endif
 
-	ast_debug(3, "gl_pathc %zu\n", globbuf.gl_pathc);
+	ast_debug(3, "gl_pathc %zu\n", (size_t)globbuf.gl_pathc);
 	if (globret == GLOB_NOSPACE) {
 		ast_log(LOG_WARNING, "XML load failure, glob expansion of pattern '%s' failed: Not enough memory\n", xmlpattern);
 		ast_free(xmlpattern);
diff --git a/makeopts.in b/makeopts.in
index a0faaee..4922c2f 100644
--- a/makeopts.in
+++ b/makeopts.in
@@ -109,6 +109,9 @@ AST_TRAMPOLINES=@AST_TRAMPOLINES@
 AST_NO_STRICT_OVERFLOW=@AST_NO_STRICT_OVERFLOW@
 AST_SHADOW_WARNINGS=@AST_SHADOW_WARNINGS@
 AST_NESTED_FUNCTIONS=@AST_NESTED_FUNCTIONS@
+AST_CLANG_BLOCKS=@AST_CLANG_BLOCKS@
+AST_CLANG_BLOCKS_LIBS=@AST_CLANG_BLOCKS_LIBS@
+C_COMPILER_FAMILY=@AST_C_COMPILER_FAMILY@
 AST_RPATH=@AST_RPATH@
 AST_FORTIFY_SOURCE=@AST_FORTIFY_SOURCE@
 AST_MARCH_NATIVE=@AST_MARCH_NATIVE@
diff --git a/menuselect/configure b/menuselect/configure
index 5081371..648091e 100755
--- a/menuselect/configure
+++ b/menuselect/configure
@@ -3227,7 +3227,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 else
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
- void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;}
+ void __attribute__(()) *test(void *muffin, ...) ;
 int
 main ()
 {
@@ -3299,7 +3299,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 else
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
- void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;}
+ void __attribute__(()) *test(void *muffin, ...) ;
 int
 main ()
 {
@@ -3371,7 +3371,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 else
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
-static void __attribute__((weakref("foo"))) *test(void *muffin, ...) {return (void *) 0;}
+static void __attribute__((weakref("foo"))) *test(void *muffin, ...) ;
 int
 main ()
 {
@@ -3445,7 +3445,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 else
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
-static void __attribute__((weakref("foo"))) *test(void *muffin, ...) {return (void *) 0;}
+static void __attribute__((weakref("foo"))) *test(void *muffin, ...) ;
 int
 main ()
 {
diff --git a/menuselect/configure.ac b/menuselect/configure.ac
index df305d5..5989f5c 100644
--- a/menuselect/configure.ac
+++ b/menuselect/configure.ac
@@ -15,7 +15,7 @@ AC_CONFIG_SRCDIR([menuselect.c])
 AC_CONFIG_HEADER(autoconfig.h)
 
 AC_COPYRIGHT("Menuselect")
-AC_REVISION($Revision: 418850 $)
+AC_REVISION($Revision$)
 
 AC_CANONICAL_BUILD
 AC_CANONICAL_HOST
diff --git a/mkinstalldirs b/mkinstalldirs
index 86809c6..6b3b5fc 100755
--- a/mkinstalldirs
+++ b/mkinstalldirs
@@ -4,7 +4,7 @@
 # Created: 1993-05-16
 # Public domain
 
-# $Id: mkinstalldirs 25628 2006-05-08 16:02:42Z kpfleming $
+# $Id$
 
 errstatus=0
 
diff --git a/pbx/Makefile b/pbx/Makefile
index 0afc4bc..a031cdf 100644
--- a/pbx/Makefile
+++ b/pbx/Makefile
@@ -24,7 +24,7 @@ ifneq ($(findstring $(OSARCH), mingw32 cygwin ),)
 endif
 
 clean::
-	rm -f ael/*.o ael/*.i
+	rm -f ael/*.o ael/*.i ael/*.gcda ael/*.gcno
 
 dundi-parser.o: dundi-parser.h
 dundi-parser.o: _ASTCFLAGS+=-I.
diff --git a/pbx/dundi-parser.c b/pbx/dundi-parser.c
index d29cbc0..c178fd6 100644
--- a/pbx/dundi-parser.c
+++ b/pbx/dundi-parser.c
@@ -28,7 +28,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 413589 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/socket.h>
 #include <netinet/in.h>
@@ -61,7 +61,7 @@ char *dundi_eid_to_str_short(char *s, int maxlen, dundi_eid *eid)
 			*s = '\0';
 	} else {
 		for (x=0;x<6;x++) {
-			sprintf(s, "%02X", (unsigned)eid->eid[x]);
+			sprintf(s, "%02hhX", (unsigned char)eid->eid[x]);
 			s += 2;
 		}
 	}
@@ -320,7 +320,7 @@ static void dump_encrypted(char *output, int maxlen, void *value, int len)
 	if ((len > 16) && !(len % 16)) {
 		/* Build up IV */
 		for (x=0;x<16;x++) {
-			snprintf(iv + (x << 1), 3, "%02x", (unsigned)((unsigned char *)value)[x]);
+			snprintf(iv + (x << 1), 3, "%02hhx", ((unsigned char *)value)[x]);
 		}
 		snprintf(output, maxlen, "[IV %s] %d encrypted blocks\n", iv, len / 16);
 	} else
@@ -334,7 +334,7 @@ static void dump_raw(char *output, int maxlen, void *value, int len)
 	output[maxlen - 1] = '\0';
 	strcpy(output, "[ ");
 	for (x=0;x<len;x++) {
-		snprintf(output + strlen(output), maxlen - strlen(output) - 1, "%02x ", (unsigned)u[x]);
+		snprintf(output + strlen(output), maxlen - strlen(output) - 1, "%02hhx ", u[x]);
 	}
 	strncat(output + strlen(output), "]", maxlen - strlen(output) - 1);
 }
@@ -464,7 +464,7 @@ void dundi_showframe(struct dundi_hdr *fhi, int rx, struct sockaddr_in *sin, int
 	} else {
 		class = commands[(int)(fhi->cmdresp & 0x3f)];
 	}
-	snprintf(subclass2, (int)sizeof(subclass2), "%02x", (unsigned)fhi->cmdflags);
+	snprintf(subclass2, (int)sizeof(subclass2), "%02hhx", (unsigned char)fhi->cmdflags);
 	subclass = subclass2;
 	snprintf(tmp, (int)sizeof(tmp), 
 		"%s-Frame -- OSeqno: %3.3d ISeqno: %3.3d Type: %s (%s)\n",
diff --git a/pbx/pbx_ael.c b/pbx/pbx_ael.c
index 6323947..5cc07cf 100644
--- a/pbx/pbx_ael.c
+++ b/pbx/pbx_ael.c
@@ -30,7 +30,7 @@
 #include "asterisk.h"
 
 #if !defined(STANDALONE)
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #endif
 
 #include <ctype.h>
diff --git a/pbx/pbx_config.c b/pbx/pbx_config.c
index 1aceb96..c4a0e6c 100644
--- a/pbx/pbx_config.c
+++ b/pbx/pbx_config.c
@@ -82,7 +82,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 427276 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <ctype.h>
 
@@ -133,8 +133,6 @@ static char *complete_dialplan_remove_context(struct ast_cli_args *);
 
 static char *handle_cli_dialplan_remove_context(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-	struct ast_context *con;
-
 	switch (cmd) {
 	case CLI_INIT:
 		e->command = "dialplan remove context";
@@ -150,16 +148,11 @@ static char *handle_cli_dialplan_remove_context(struct ast_cli_entry *e, int cmd
 		return CLI_SHOWUSAGE;
 	}
 
-	con = ast_context_find(a->argv[3]);
-
-	if (!con) {
-		ast_cli(a->fd, "There is no such context as '%s'\n",
-                        a->argv[3]);
-                return CLI_SUCCESS;
+	if (ast_context_destroy_by_name(a->argv[3], NULL)) {
+		ast_cli(a->fd, "There is no such context as '%s'\n", a->argv[3]);
+		return CLI_SUCCESS;
 	} else {
-		ast_context_destroy(con, registrar);
-		ast_cli(a->fd, "Removing context '%s'\n",
-			a->argv[3]);
+		ast_cli(a->fd, "Removed context '%s'\n", a->argv[3]);
 		return CLI_SUCCESS;
 	}
 }
@@ -956,7 +949,9 @@ static char *handle_cli_dialplan_save(struct ast_cli_entry *e, int cmd, struct a
 					const char *el = ast_get_extension_label(p);
 					char label[128] = "";
 					char *appdata = ast_get_extension_app_data(p);
-					char *escaped;
+
+					int escaped_len = (!ast_strlen_zero(appdata)) ? 2 * strlen(appdata) + 1 : 1;
+					char escaped[escaped_len];
 
 					if (ast_get_extension_matchcid(p)) {
 						sep = "/";
@@ -970,12 +965,9 @@ static char *handle_cli_dialplan_save(struct ast_cli_entry *e, int cmd, struct a
 					}
 
 					if (!ast_strlen_zero(appdata)) {
-						int escaped_len = 2 * strlen(appdata) + 1;
-						char escaped[escaped_len];
-
 						ast_escape_semicolons(appdata, escaped, escaped_len);
 					} else {
-						escaped = "";
+						escaped[0] = '\0';
 					}
 
 					fprintf(output, "exten => %s%s%s,%d%s,%s(%s)\n",
@@ -2081,9 +2073,9 @@ static int load_module(void)
 		ast_cli_register(&cli_dialplan_save);
 	ast_cli_register_multiple(cli_pbx_config, ARRAY_LEN(cli_pbx_config));
 
-	res = ast_manager_register_xml_core(AMI_EXTENSION_ADD,
+	res = ast_manager_register_xml(AMI_EXTENSION_ADD,
 		EVENT_FLAG_SYSTEM, manager_dialplan_extension_add);
-	res |= ast_manager_register_xml_core(AMI_EXTENSION_REMOVE,
+	res |= ast_manager_register_xml(AMI_EXTENSION_REMOVE,
 		EVENT_FLAG_SYSTEM, manager_dialplan_extension_remove);
 
 	if (res) {
diff --git a/pbx/pbx_dundi.c b/pbx/pbx_dundi.c
index 8807dd0..04da247 100644
--- a/pbx/pbx_dundi.c
+++ b/pbx/pbx_dundi.c
@@ -38,7 +38,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/network.h"
 #include <sys/ioctl.h>
@@ -3264,6 +3264,7 @@ static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, i
 	pack = ast_calloc(1, len);
 	if (pack) {
 		pack->h = (struct dundi_hdr *)(pack->data);
+		pack->retransid = -1;
 		if (cmdresp != DUNDI_COMMAND_ACK) {
 			pack->retransid = ast_sched_add(sched, trans->retranstimer, dundi_rexmit, pack);
 			pack->retrans = DUNDI_DEFAULT_RETRANS - 1;
diff --git a/pbx/pbx_loopback.c b/pbx/pbx_loopback.c
index cc2140d..bd87b1f 100644
--- a/pbx/pbx_loopback.c
+++ b/pbx/pbx_loopback.c
@@ -28,7 +28,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428789 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/file.h"
 #include "asterisk/logger.h"
diff --git a/pbx/pbx_lua.c b/pbx/pbx_lua.c
index 3ff482f..77762e2 100644
--- a/pbx/pbx_lua.c
+++ b/pbx/pbx_lua.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420149 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/logger.h"
 #include "asterisk/channel.h"
diff --git a/pbx/pbx_realtime.c b/pbx/pbx_realtime.c
index 4a9d221..6c8e671 100644
--- a/pbx/pbx_realtime.c
+++ b/pbx/pbx_realtime.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 425384 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <signal.h>
 
diff --git a/pbx/pbx_spool.c b/pbx/pbx_spool.c
index b26765f..4228be9 100644
--- a/pbx/pbx_spool.c
+++ b/pbx/pbx_spool.c
@@ -28,7 +28,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419044 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/stat.h>
 #include <time.h>
@@ -102,6 +102,14 @@ struct outgoing {
 };
 
 #if defined(HAVE_INOTIFY) || defined(HAVE_KQUEUE)
+struct direntry {
+	AST_LIST_ENTRY(direntry) list;
+	time_t mtime;
+	char name[0];
+};
+
+static AST_LIST_HEAD_STATIC(dirlist, direntry);
+
 static void queue_file(const char *filename, time_t when);
 #endif
 
@@ -323,6 +331,10 @@ static int remove_from_queue(struct outgoing *o, const char *status)
 	char newfn[256];
 	const char *bname;
 
+#if defined(HAVE_INOTIFY) || defined(HAVE_KQUEUE)
+	struct direntry *cur;
+#endif
+
 	if (!ast_test_flag(&o->options, SPOOL_FLAG_ALWAYS_DELETE)) {
 		struct stat current_file_status;
 
@@ -333,6 +345,19 @@ static int remove_from_queue(struct outgoing *o, const char *status)
 		}
 	}
 
+#if defined(HAVE_INOTIFY) || defined(HAVE_KQUEUE)
+	AST_LIST_LOCK(&dirlist);
+	AST_LIST_TRAVERSE_SAFE_BEGIN(&dirlist, cur, list) {
+		if (!strcmp(cur->name, o->fn)) {
+			AST_LIST_REMOVE_CURRENT(list);
+			ast_free(cur);
+			break;
+		}
+	}
+	AST_LIST_TRAVERSE_SAFE_END;
+	AST_LIST_UNLOCK(&dirlist);
+#endif
+
 	if (!ast_test_flag(&o->options, SPOOL_FLAG_ARCHIVE)) {
 		unlink(o->fn);
 		return 0;
@@ -377,14 +402,12 @@ static void *attempt_thread(void *data)
 		res = ast_pbx_outgoing_app(o->tech, o->capabilities, o->dest, o->waittime * 1000,
 			o->app, o->data, &reason, 2 /* wait to finish */, o->cid_num, o->cid_name,
 			o->vars, o->account, NULL, NULL);
-		o->vars = NULL;
 	} else {
 		ast_verb(3, "Attempting call on %s/%s for %s@%s:%d (Retry %d)\n", o->tech, o->dest, o->exten, o->context,o->priority, o->retries);
 		res = ast_pbx_outgoing_exten(o->tech, o->capabilities, o->dest,
 			o->waittime * 1000, o->context, o->exten, o->priority, &reason,
 			2 /* wait to finish */, o->cid_num, o->cid_name, o->vars, o->account, NULL,
 			ast_test_flag(&o->options, SPOOL_FLAG_EARLY_MEDIA), NULL);
-		o->vars = NULL;
 	}
 	if (res) {
 		ast_log(LOG_NOTICE, "Call failed to go through, reason (%d) %s\n", reason, ast_channel_reason2str(reason));
@@ -488,14 +511,6 @@ static int scan_service(const char *fn, time_t now)
 	return 0;
 }
 
-#if defined(HAVE_INOTIFY) || defined(HAVE_KQUEUE)
-struct direntry {
-	AST_LIST_ENTRY(direntry) list;
-	time_t mtime;
-	char name[0];
-};
-
-static AST_LIST_HEAD_STATIC(dirlist, direntry);
 
 #if defined(HAVE_INOTIFY)
 /* Only one thread is accessing this list, so no lock is necessary */
@@ -503,6 +518,8 @@ static AST_LIST_HEAD_NOLOCK_STATIC(createlist, direntry);
 static AST_LIST_HEAD_NOLOCK_STATIC(openlist, direntry);
 #endif
 
+#if defined(HAVE_INOTIFY) || defined(HAVE_KQUEUE)
+
 static void queue_file(const char *filename, time_t when)
 {
 	struct stat st;
diff --git a/res/Makefile b/res/Makefile
index 4038634..dbab999 100644
--- a/res/Makefile
+++ b/res/Makefile
@@ -33,6 +33,9 @@ ael/ael_lex.o: _ASTCFLAGS+=-I. -Iael -Wno-unused
 
 ael/ael.tab.o: ael/ael.tab.c ael/ael.tab.h ../include/asterisk/ael_structs.h
 ael/ael.tab.o: _ASTCFLAGS+=-I. -Iael -DYYENABLE_NLS=0
+	ifneq ($(AST_CLANG_BLOCKS),)
+		_ASTCFLAGS+=-Wno-parentheses-equality
+	endif
 
 $(if $(filter res_ais,$(EMBEDDED_MODS)),modules.link,res_ais.so): ais/clm.o ais/evt.o
 ais/clm.o ais/evt.o: _ASTCFLAGS+=$(call MOD_ASTCFLAGS,res_ais)
@@ -73,9 +76,11 @@ endif
 ael/pval.o: ael/pval.c
 
 clean::
-	rm -f snmp/*.[oi] ael/*.[oi] ais/*.[oi] ari/*.[oi]
-	rm -f res_pjsip/*.[oi] stasis/*.[oi]
-	rm -f parking/*.o parking/*.i stasis_recording/*.[oi]
+	rm -f snmp/*.o snmp/*.i ael/*.o ael/*.i ais/*.o ais/*.i snmp/*.gcda snmp/*.gcno ael/*.gcda ael/*.gcno
+	rm -f res_pjsip/*.[oi] res_pjsip/*.gcda res_pjsip/*.gcno
+	rm -f stasis/*.[oi] stasis/*.gcda stasis/*.gcno
+	rm -f parking/*.[oi] parking/*.gcda parking/*.gcno
+	rm -f stasis_recording/*.[oi] stasis_recording/*.gcda stasis_recording/*.gcno
 
 $(if $(filter res_parking,$(EMBEDDED_MODS)),modules.link,res_parking.so): $(subst .c,.o,$(wildcard parking/*.c))
 $(subst .c,.o,$(wildcard parking/*.c)): _ASTCFLAGS+=$(call MOD_ASTCFLAGS,res_parking)
diff --git a/res/ael/ael.flex b/res/ael/ael.flex
index 0275e16..0b6ec32 100644
--- a/res/ael/ael.flex
+++ b/res/ael/ael.flex
@@ -69,7 +69,7 @@
 
 %{
 #include "asterisk.h"
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 410994 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/types.h>
 #include <sys/stat.h>
diff --git a/res/ael/ael.tab.c b/res/ael/ael.tab.c
index 6594f29..c838d79 100644
--- a/res/ael/ael.tab.c
+++ b/res/ael/ael.tab.c
@@ -101,7 +101,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 389251 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <stdio.h>
 #include <stdlib.h>
diff --git a/res/ael/ael.y b/res/ael/ael.y
index 8eaf8dd..27e04c5 100644
--- a/res/ael/ael.y
+++ b/res/ael/ael.y
@@ -24,7 +24,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 272260 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <stdio.h>
 #include <stdlib.h>
diff --git a/res/ael/ael_lex.c b/res/ael/ael_lex.c
index e91e63f..6aec03b 100644
--- a/res/ael/ael_lex.c
+++ b/res/ael/ael_lex.c
@@ -828,7 +828,7 @@ static yyconst flex_int16_t yy_chk[1073] =
  */
 #line 71 "ael.flex"
 #include "asterisk.h"
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 410994 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/types.h>
 #include <sys/stat.h>
diff --git a/res/ael/pval.c b/res/ael/pval.c
index 819f9f0..d72ef0d 100644
--- a/res/ael/pval.c
+++ b/res/ael/pval.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 418019 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/types.h>
 #include <stdlib.h>
diff --git a/res/ari.make b/res/ari.make
index f9a87d3..133f2ae 100644
--- a/res/ari.make
+++ b/res/ari.make
@@ -18,46 +18,57 @@
 #
 
 res_ari_asterisk.so: ari/resource_asterisk.o
+.res_ari_asterisk.moduleinfo: ari/resource_asterisk.c
 
 ari/resource_asterisk.o: _ASTCFLAGS+=$(call MOD_ASTCFLAGS,res_ari_asterisk)
 
 res_ari_endpoints.so: ari/resource_endpoints.o
+.res_ari_endpoints.moduleinfo: ari/resource_endpoints.c
 
 ari/resource_endpoints.o: _ASTCFLAGS+=$(call MOD_ASTCFLAGS,res_ari_endpoints)
 
 res_ari_channels.so: ari/resource_channels.o
+.res_ari_channels.moduleinfo: ari/resource_channels.c
 
 ari/resource_channels.o: _ASTCFLAGS+=$(call MOD_ASTCFLAGS,res_ari_channels)
 
 res_ari_bridges.so: ari/resource_bridges.o
+.res_ari_bridges.moduleinfo: ari/resource_bridges.c
 
 ari/resource_bridges.o: _ASTCFLAGS+=$(call MOD_ASTCFLAGS,res_ari_bridges)
 
 res_ari_recordings.so: ari/resource_recordings.o
+.res_ari_recordings.moduleinfo: ari/resource_recordings.c
 
 ari/resource_recordings.o: _ASTCFLAGS+=$(call MOD_ASTCFLAGS,res_ari_recordings)
 
 res_ari_sounds.so: ari/resource_sounds.o
+.res_ari_sounds.moduleinfo: ari/resource_sounds.c
 
 ari/resource_sounds.o: _ASTCFLAGS+=$(call MOD_ASTCFLAGS,res_ari_sounds)
 
 res_ari_playbacks.so: ari/resource_playbacks.o
+.res_ari_playbacks.moduleinfo: ari/resource_playbacks.c
 
 ari/resource_playbacks.o: _ASTCFLAGS+=$(call MOD_ASTCFLAGS,res_ari_playbacks)
 
 res_ari_device_states.so: ari/resource_device_states.o
+.res_ari_device_states.moduleinfo: ari/resource_device_states.c
 
 ari/resource_device_states.o: _ASTCFLAGS+=$(call MOD_ASTCFLAGS,res_ari_device_states)
 
 res_ari_mailboxes.so: ari/resource_mailboxes.o
+.res_ari_mailboxes.moduleinfo: ari/resource_mailboxes.c
 
 ari/resource_mailboxes.o: _ASTCFLAGS+=$(call MOD_ASTCFLAGS,res_ari_mailboxes)
 
 res_ari_events.so: ari/resource_events.o
+.res_ari_events.moduleinfo: ari/resource_events.c
 
 ari/resource_events.o: _ASTCFLAGS+=$(call MOD_ASTCFLAGS,res_ari_events)
 
 res_ari_applications.so: ari/resource_applications.o
+.res_ari_applications.moduleinfo: ari/resource_applications.c
 
 ari/resource_applications.o: _ASTCFLAGS+=$(call MOD_ASTCFLAGS,res_ari_applications)
 
diff --git a/res/ari/ari_model_validators.c b/res/ari/ari_model_validators.c
index c471eee..389f83b 100644
--- a/res/ari/ari_model_validators.c
+++ b/res/ari/ari_model_validators.c
@@ -24,12 +24,12 @@
  * !!!!!                               DO NOT EDIT                        !!!!!
  * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  * This file is generated by a mustache template. Please see the original
- * template in rest-api-templates/ari_model_validators.h.mustache
+ * template in rest-api-templates/ari_model_validators.c.mustache
  */
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 429064 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/logger.h"
 #include "asterisk/module.h"
@@ -308,6 +308,248 @@ ari_validator ast_ari_validate_config_info_fn(void)
 	return ast_ari_validate_config_info;
 }
 
+int ast_ari_validate_config_tuple(struct ast_json *json)
+{
+	int res = 1;
+	struct ast_json_iter *iter;
+	int has_attribute = 0;
+	int has_value = 0;
+
+	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("attribute", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_attribute = 1;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI ConfigTuple field attribute failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("value", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_value = 1;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI ConfigTuple field value failed validation\n");
+				res = 0;
+			}
+		} else
+		{
+			ast_log(LOG_ERROR,
+				"ARI ConfigTuple has undocumented field %s\n",
+				ast_json_object_iter_key(iter));
+			res = 0;
+		}
+	}
+
+	if (!has_attribute) {
+		ast_log(LOG_ERROR, "ARI ConfigTuple missing required field attribute\n");
+		res = 0;
+	}
+
+	if (!has_value) {
+		ast_log(LOG_ERROR, "ARI ConfigTuple missing required field value\n");
+		res = 0;
+	}
+
+	return res;
+}
+
+ari_validator ast_ari_validate_config_tuple_fn(void)
+{
+	return ast_ari_validate_config_tuple;
+}
+
+int ast_ari_validate_log_channel(struct ast_json *json)
+{
+	int res = 1;
+	struct ast_json_iter *iter;
+	int has_channel = 0;
+	int has_configuration = 0;
+	int has_status = 0;
+	int has_type = 0;
+
+	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("channel", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_channel = 1;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI LogChannel field channel failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("configuration", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_configuration = 1;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI LogChannel field configuration failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("status", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_status = 1;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI LogChannel field status failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_type = 1;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI LogChannel field type failed validation\n");
+				res = 0;
+			}
+		} else
+		{
+			ast_log(LOG_ERROR,
+				"ARI LogChannel has undocumented field %s\n",
+				ast_json_object_iter_key(iter));
+			res = 0;
+		}
+	}
+
+	if (!has_channel) {
+		ast_log(LOG_ERROR, "ARI LogChannel missing required field channel\n");
+		res = 0;
+	}
+
+	if (!has_configuration) {
+		ast_log(LOG_ERROR, "ARI LogChannel missing required field configuration\n");
+		res = 0;
+	}
+
+	if (!has_status) {
+		ast_log(LOG_ERROR, "ARI LogChannel missing required field status\n");
+		res = 0;
+	}
+
+	if (!has_type) {
+		ast_log(LOG_ERROR, "ARI LogChannel missing required field type\n");
+		res = 0;
+	}
+
+	return res;
+}
+
+ari_validator ast_ari_validate_log_channel_fn(void)
+{
+	return ast_ari_validate_log_channel;
+}
+
+int ast_ari_validate_module(struct ast_json *json)
+{
+	int res = 1;
+	struct ast_json_iter *iter;
+	int has_description = 0;
+	int has_name = 0;
+	int has_status = 0;
+	int has_support_level = 0;
+	int has_use_count = 0;
+
+	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("description", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_description = 1;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI Module field description failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("name", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_name = 1;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI Module field name failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("status", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_status = 1;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI Module field status failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("support_level", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_support_level = 1;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI Module field support_level failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("use_count", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_use_count = 1;
+			prop_is_valid = ast_ari_validate_int(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI Module field use_count failed validation\n");
+				res = 0;
+			}
+		} else
+		{
+			ast_log(LOG_ERROR,
+				"ARI Module has undocumented field %s\n",
+				ast_json_object_iter_key(iter));
+			res = 0;
+		}
+	}
+
+	if (!has_description) {
+		ast_log(LOG_ERROR, "ARI Module missing required field description\n");
+		res = 0;
+	}
+
+	if (!has_name) {
+		ast_log(LOG_ERROR, "ARI Module missing required field name\n");
+		res = 0;
+	}
+
+	if (!has_status) {
+		ast_log(LOG_ERROR, "ARI Module missing required field status\n");
+		res = 0;
+	}
+
+	if (!has_support_level) {
+		ast_log(LOG_ERROR, "ARI Module missing required field support_level\n");
+		res = 0;
+	}
+
+	if (!has_use_count) {
+		ast_log(LOG_ERROR, "ARI Module missing required field use_count\n");
+		res = 0;
+	}
+
+	return res;
+}
+
+ari_validator ast_ari_validate_module_fn(void)
+{
+	return ast_ari_validate_module;
+}
+
 int ast_ari_validate_set_id(struct ast_json *json)
 {
 	int res = 1;
@@ -786,6 +1028,7 @@ int ast_ari_validate_channel(struct ast_json *json)
 	int has_creationtime = 0;
 	int has_dialplan = 0;
 	int has_id = 0;
+	int has_language = 0;
 	int has_name = 0;
 	int has_state = 0;
 
@@ -850,6 +1093,16 @@ int ast_ari_validate_channel(struct ast_json *json)
 				res = 0;
 			}
 		} else
+		if (strcmp("language", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_language = 1;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI Channel field language failed validation\n");
+				res = 0;
+			}
+		} else
 		if (strcmp("name", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
 			has_name = 1;
@@ -908,6 +1161,11 @@ int ast_ari_validate_channel(struct ast_json *json)
 		res = 0;
 	}
 
+	if (!has_language) {
+		ast_log(LOG_ERROR, "ARI Channel missing required field language\n");
+		res = 0;
+	}
+
 	if (!has_name) {
 		ast_log(LOG_ERROR, "ARI Channel missing required field name\n");
 		res = 0;
@@ -3181,13 +3439,12 @@ ari_validator ast_ari_validate_channel_hangup_request_fn(void)
 	return ast_ari_validate_channel_hangup_request;
 }
 
-int ast_ari_validate_channel_left_bridge(struct ast_json *json)
+int ast_ari_validate_channel_hold(struct ast_json *json)
 {
 	int res = 1;
 	struct ast_json_iter *iter;
 	int has_type = 0;
 	int has_application = 0;
-	int has_bridge = 0;
 	int has_channel = 0;
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
@@ -3197,7 +3454,7 @@ int ast_ari_validate_channel_left_bridge(struct ast_json *json)
 			prop_is_valid = ast_ari_validate_string(
 				ast_json_object_iter_value(iter));
 			if (!prop_is_valid) {
-				ast_log(LOG_ERROR, "ARI ChannelLeftBridge field type failed validation\n");
+				ast_log(LOG_ERROR, "ARI ChannelHold field type failed validation\n");
 				res = 0;
 			}
 		} else
@@ -3207,7 +3464,7 @@ int ast_ari_validate_channel_left_bridge(struct ast_json *json)
 			prop_is_valid = ast_ari_validate_string(
 				ast_json_object_iter_value(iter));
 			if (!prop_is_valid) {
-				ast_log(LOG_ERROR, "ARI ChannelLeftBridge field application failed validation\n");
+				ast_log(LOG_ERROR, "ARI ChannelHold field application failed validation\n");
 				res = 0;
 			}
 		} else
@@ -3216,72 +3473,67 @@ int ast_ari_validate_channel_left_bridge(struct ast_json *json)
 			prop_is_valid = ast_ari_validate_date(
 				ast_json_object_iter_value(iter));
 			if (!prop_is_valid) {
-				ast_log(LOG_ERROR, "ARI ChannelLeftBridge field timestamp failed validation\n");
+				ast_log(LOG_ERROR, "ARI ChannelHold field timestamp failed validation\n");
 				res = 0;
 			}
 		} else
-		if (strcmp("bridge", ast_json_object_iter_key(iter)) == 0) {
+		if (strcmp("channel", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
-			has_bridge = 1;
-			prop_is_valid = ast_ari_validate_bridge(
+			has_channel = 1;
+			prop_is_valid = ast_ari_validate_channel(
 				ast_json_object_iter_value(iter));
 			if (!prop_is_valid) {
-				ast_log(LOG_ERROR, "ARI ChannelLeftBridge field bridge failed validation\n");
+				ast_log(LOG_ERROR, "ARI ChannelHold field channel failed validation\n");
 				res = 0;
 			}
 		} else
-		if (strcmp("channel", ast_json_object_iter_key(iter)) == 0) {
+		if (strcmp("musicclass", ast_json_object_iter_key(iter)) == 0) {
 			int prop_is_valid;
-			has_channel = 1;
-			prop_is_valid = ast_ari_validate_channel(
+			prop_is_valid = ast_ari_validate_string(
 				ast_json_object_iter_value(iter));
 			if (!prop_is_valid) {
-				ast_log(LOG_ERROR, "ARI ChannelLeftBridge field channel failed validation\n");
+				ast_log(LOG_ERROR, "ARI ChannelHold field musicclass failed validation\n");
 				res = 0;
 			}
 		} else
 		{
 			ast_log(LOG_ERROR,
-				"ARI ChannelLeftBridge has undocumented field %s\n",
+				"ARI ChannelHold has undocumented field %s\n",
 				ast_json_object_iter_key(iter));
 			res = 0;
 		}
 	}
 
 	if (!has_type) {
-		ast_log(LOG_ERROR, "ARI ChannelLeftBridge missing required field type\n");
+		ast_log(LOG_ERROR, "ARI ChannelHold missing required field type\n");
 		res = 0;
 	}
 
 	if (!has_application) {
-		ast_log(LOG_ERROR, "ARI ChannelLeftBridge missing required field application\n");
-		res = 0;
-	}
-
-	if (!has_bridge) {
-		ast_log(LOG_ERROR, "ARI ChannelLeftBridge missing required field bridge\n");
+		ast_log(LOG_ERROR, "ARI ChannelHold missing required field application\n");
 		res = 0;
 	}
 
 	if (!has_channel) {
-		ast_log(LOG_ERROR, "ARI ChannelLeftBridge missing required field channel\n");
+		ast_log(LOG_ERROR, "ARI ChannelHold missing required field channel\n");
 		res = 0;
 	}
 
 	return res;
 }
 
-ari_validator ast_ari_validate_channel_left_bridge_fn(void)
+ari_validator ast_ari_validate_channel_hold_fn(void)
 {
-	return ast_ari_validate_channel_left_bridge;
+	return ast_ari_validate_channel_hold;
 }
 
-int ast_ari_validate_channel_state_change(struct ast_json *json)
+int ast_ari_validate_channel_left_bridge(struct ast_json *json)
 {
 	int res = 1;
 	struct ast_json_iter *iter;
 	int has_type = 0;
 	int has_application = 0;
+	int has_bridge = 0;
 	int has_channel = 0;
 
 	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
@@ -3291,7 +3543,7 @@ int ast_ari_validate_channel_state_change(struct ast_json *json)
 			prop_is_valid = ast_ari_validate_string(
 				ast_json_object_iter_value(iter));
 			if (!prop_is_valid) {
-				ast_log(LOG_ERROR, "ARI ChannelStateChange field type failed validation\n");
+				ast_log(LOG_ERROR, "ARI ChannelLeftBridge field type failed validation\n");
 				res = 0;
 			}
 		} else
@@ -3301,7 +3553,7 @@ int ast_ari_validate_channel_state_change(struct ast_json *json)
 			prop_is_valid = ast_ari_validate_string(
 				ast_json_object_iter_value(iter));
 			if (!prop_is_valid) {
-				ast_log(LOG_ERROR, "ARI ChannelStateChange field application failed validation\n");
+				ast_log(LOG_ERROR, "ARI ChannelLeftBridge field application failed validation\n");
 				res = 0;
 			}
 		} else
@@ -3310,7 +3562,17 @@ int ast_ari_validate_channel_state_change(struct ast_json *json)
 			prop_is_valid = ast_ari_validate_date(
 				ast_json_object_iter_value(iter));
 			if (!prop_is_valid) {
-				ast_log(LOG_ERROR, "ARI ChannelStateChange field timestamp failed validation\n");
+				ast_log(LOG_ERROR, "ARI ChannelLeftBridge field timestamp failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("bridge", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_bridge = 1;
+			prop_is_valid = ast_ari_validate_bridge(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI ChannelLeftBridge field bridge failed validation\n");
 				res = 0;
 			}
 		} else
@@ -3320,34 +3582,118 @@ int ast_ari_validate_channel_state_change(struct ast_json *json)
 			prop_is_valid = ast_ari_validate_channel(
 				ast_json_object_iter_value(iter));
 			if (!prop_is_valid) {
-				ast_log(LOG_ERROR, "ARI ChannelStateChange field channel failed validation\n");
+				ast_log(LOG_ERROR, "ARI ChannelLeftBridge field channel failed validation\n");
 				res = 0;
 			}
 		} else
 		{
 			ast_log(LOG_ERROR,
-				"ARI ChannelStateChange has undocumented field %s\n",
+				"ARI ChannelLeftBridge has undocumented field %s\n",
 				ast_json_object_iter_key(iter));
 			res = 0;
 		}
 	}
 
 	if (!has_type) {
-		ast_log(LOG_ERROR, "ARI ChannelStateChange missing required field type\n");
+		ast_log(LOG_ERROR, "ARI ChannelLeftBridge missing required field type\n");
 		res = 0;
 	}
 
 	if (!has_application) {
-		ast_log(LOG_ERROR, "ARI ChannelStateChange missing required field application\n");
+		ast_log(LOG_ERROR, "ARI ChannelLeftBridge missing required field application\n");
 		res = 0;
 	}
 
-	if (!has_channel) {
-		ast_log(LOG_ERROR, "ARI ChannelStateChange missing required field channel\n");
+	if (!has_bridge) {
+		ast_log(LOG_ERROR, "ARI ChannelLeftBridge missing required field bridge\n");
 		res = 0;
 	}
 
-	return res;
+	if (!has_channel) {
+		ast_log(LOG_ERROR, "ARI ChannelLeftBridge missing required field channel\n");
+		res = 0;
+	}
+
+	return res;
+}
+
+ari_validator ast_ari_validate_channel_left_bridge_fn(void)
+{
+	return ast_ari_validate_channel_left_bridge;
+}
+
+int ast_ari_validate_channel_state_change(struct ast_json *json)
+{
+	int res = 1;
+	struct ast_json_iter *iter;
+	int has_type = 0;
+	int has_application = 0;
+	int has_channel = 0;
+
+	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_type = 1;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI ChannelStateChange field type failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("application", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_application = 1;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI ChannelStateChange field application failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_date(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI ChannelStateChange field timestamp failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("channel", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_channel = 1;
+			prop_is_valid = ast_ari_validate_channel(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI ChannelStateChange field channel failed validation\n");
+				res = 0;
+			}
+		} else
+		{
+			ast_log(LOG_ERROR,
+				"ARI ChannelStateChange has undocumented field %s\n",
+				ast_json_object_iter_key(iter));
+			res = 0;
+		}
+	}
+
+	if (!has_type) {
+		ast_log(LOG_ERROR, "ARI ChannelStateChange missing required field type\n");
+		res = 0;
+	}
+
+	if (!has_application) {
+		ast_log(LOG_ERROR, "ARI ChannelStateChange missing required field application\n");
+		res = 0;
+	}
+
+	if (!has_channel) {
+		ast_log(LOG_ERROR, "ARI ChannelStateChange missing required field channel\n");
+		res = 0;
+	}
+
+	return res;
 }
 
 ari_validator ast_ari_validate_channel_state_change_fn(void)
@@ -3529,6 +3875,85 @@ ari_validator ast_ari_validate_channel_talking_started_fn(void)
 	return ast_ari_validate_channel_talking_started;
 }
 
+int ast_ari_validate_channel_unhold(struct ast_json *json)
+{
+	int res = 1;
+	struct ast_json_iter *iter;
+	int has_type = 0;
+	int has_application = 0;
+	int has_channel = 0;
+
+	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_type = 1;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI ChannelUnhold field type failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("application", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_application = 1;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI ChannelUnhold field application failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_date(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI ChannelUnhold field timestamp failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("channel", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_channel = 1;
+			prop_is_valid = ast_ari_validate_channel(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI ChannelUnhold field channel failed validation\n");
+				res = 0;
+			}
+		} else
+		{
+			ast_log(LOG_ERROR,
+				"ARI ChannelUnhold has undocumented field %s\n",
+				ast_json_object_iter_key(iter));
+			res = 0;
+		}
+	}
+
+	if (!has_type) {
+		ast_log(LOG_ERROR, "ARI ChannelUnhold missing required field type\n");
+		res = 0;
+	}
+
+	if (!has_application) {
+		ast_log(LOG_ERROR, "ARI ChannelUnhold missing required field application\n");
+		res = 0;
+	}
+
+	if (!has_channel) {
+		ast_log(LOG_ERROR, "ARI ChannelUnhold missing required field channel\n");
+		res = 0;
+	}
+
+	return res;
+}
+
+ari_validator ast_ari_validate_channel_unhold_fn(void)
+{
+	return ast_ari_validate_channel_unhold;
+}
+
 int ast_ari_validate_channel_userevent(struct ast_json *json)
 {
 	int res = 1;
@@ -3755,6 +4180,180 @@ ari_validator ast_ari_validate_channel_varset_fn(void)
 	return ast_ari_validate_channel_varset;
 }
 
+int ast_ari_validate_contact_info(struct ast_json *json)
+{
+	int res = 1;
+	struct ast_json_iter *iter;
+	int has_aor = 0;
+	int has_contact_status = 0;
+	int has_uri = 0;
+
+	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("aor", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_aor = 1;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI ContactInfo field aor failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("contact_status", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_contact_status = 1;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI ContactInfo field contact_status failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("roundtrip_usec", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI ContactInfo field roundtrip_usec failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("uri", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_uri = 1;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI ContactInfo field uri failed validation\n");
+				res = 0;
+			}
+		} else
+		{
+			ast_log(LOG_ERROR,
+				"ARI ContactInfo has undocumented field %s\n",
+				ast_json_object_iter_key(iter));
+			res = 0;
+		}
+	}
+
+	if (!has_aor) {
+		ast_log(LOG_ERROR, "ARI ContactInfo missing required field aor\n");
+		res = 0;
+	}
+
+	if (!has_contact_status) {
+		ast_log(LOG_ERROR, "ARI ContactInfo missing required field contact_status\n");
+		res = 0;
+	}
+
+	if (!has_uri) {
+		ast_log(LOG_ERROR, "ARI ContactInfo missing required field uri\n");
+		res = 0;
+	}
+
+	return res;
+}
+
+ari_validator ast_ari_validate_contact_info_fn(void)
+{
+	return ast_ari_validate_contact_info;
+}
+
+int ast_ari_validate_contact_status_change(struct ast_json *json)
+{
+	int res = 1;
+	struct ast_json_iter *iter;
+	int has_type = 0;
+	int has_application = 0;
+	int has_contact_info = 0;
+	int has_endpoint = 0;
+
+	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_type = 1;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI ContactStatusChange field type failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("application", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_application = 1;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI ContactStatusChange field application failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_date(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI ContactStatusChange field timestamp failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("contact_info", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_contact_info = 1;
+			prop_is_valid = ast_ari_validate_contact_info(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI ContactStatusChange field contact_info failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("endpoint", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_endpoint = 1;
+			prop_is_valid = ast_ari_validate_endpoint(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI ContactStatusChange field endpoint failed validation\n");
+				res = 0;
+			}
+		} else
+		{
+			ast_log(LOG_ERROR,
+				"ARI ContactStatusChange has undocumented field %s\n",
+				ast_json_object_iter_key(iter));
+			res = 0;
+		}
+	}
+
+	if (!has_type) {
+		ast_log(LOG_ERROR, "ARI ContactStatusChange missing required field type\n");
+		res = 0;
+	}
+
+	if (!has_application) {
+		ast_log(LOG_ERROR, "ARI ContactStatusChange missing required field application\n");
+		res = 0;
+	}
+
+	if (!has_contact_info) {
+		ast_log(LOG_ERROR, "ARI ContactStatusChange missing required field contact_info\n");
+		res = 0;
+	}
+
+	if (!has_endpoint) {
+		ast_log(LOG_ERROR, "ARI ContactStatusChange missing required field endpoint\n");
+		res = 0;
+	}
+
+	return res;
+}
+
+ari_validator ast_ari_validate_contact_status_change_fn(void)
+{
+	return ast_ari_validate_contact_status_change;
+}
+
 int ast_ari_validate_device_state_changed(struct ast_json *json)
 {
 	int res = 1;
@@ -4054,7 +4653,7 @@ int ast_ari_validate_event(struct ast_json *json)
 
 	discriminator = ast_json_string_get(ast_json_object_get(json, "type"));
 	if (!discriminator) {
-		ast_log(LOG_ERROR, "ARI Event missing required field type");
+		ast_log(LOG_ERROR, "ARI Event missing required field type\n");
 		return 0;
 	}
 
@@ -4103,6 +4702,9 @@ int ast_ari_validate_event(struct ast_json *json)
 	if (strcmp("ChannelHangupRequest", discriminator) == 0) {
 		return ast_ari_validate_channel_hangup_request(json);
 	} else
+	if (strcmp("ChannelHold", discriminator) == 0) {
+		return ast_ari_validate_channel_hold(json);
+	} else
 	if (strcmp("ChannelLeftBridge", discriminator) == 0) {
 		return ast_ari_validate_channel_left_bridge(json);
 	} else
@@ -4115,12 +4717,18 @@ int ast_ari_validate_event(struct ast_json *json)
 	if (strcmp("ChannelTalkingStarted", discriminator) == 0) {
 		return ast_ari_validate_channel_talking_started(json);
 	} else
+	if (strcmp("ChannelUnhold", discriminator) == 0) {
+		return ast_ari_validate_channel_unhold(json);
+	} else
 	if (strcmp("ChannelUserevent", discriminator) == 0) {
 		return ast_ari_validate_channel_userevent(json);
 	} else
 	if (strcmp("ChannelVarset", discriminator) == 0) {
 		return ast_ari_validate_channel_varset(json);
 	} else
+	if (strcmp("ContactStatusChange", discriminator) == 0) {
+		return ast_ari_validate_contact_status_change(json);
+	} else
 	if (strcmp("DeviceStateChanged", discriminator) == 0) {
 		return ast_ari_validate_device_state_changed(json);
 	} else
@@ -4130,6 +4738,9 @@ int ast_ari_validate_event(struct ast_json *json)
 	if (strcmp("EndpointStateChange", discriminator) == 0) {
 		return ast_ari_validate_endpoint_state_change(json);
 	} else
+	if (strcmp("PeerStatusChange", discriminator) == 0) {
+		return ast_ari_validate_peer_status_change(json);
+	} else
 	if (strcmp("PlaybackFinished", discriminator) == 0) {
 		return ast_ari_validate_playback_finished(json);
 	} else
@@ -4225,7 +4836,7 @@ int ast_ari_validate_message(struct ast_json *json)
 
 	discriminator = ast_json_string_get(ast_json_object_get(json, "type"));
 	if (!discriminator) {
-		ast_log(LOG_ERROR, "ARI Message missing required field type");
+		ast_log(LOG_ERROR, "ARI Message missing required field type\n");
 		return 0;
 	}
 
@@ -4274,6 +4885,9 @@ int ast_ari_validate_message(struct ast_json *json)
 	if (strcmp("ChannelHangupRequest", discriminator) == 0) {
 		return ast_ari_validate_channel_hangup_request(json);
 	} else
+	if (strcmp("ChannelHold", discriminator) == 0) {
+		return ast_ari_validate_channel_hold(json);
+	} else
 	if (strcmp("ChannelLeftBridge", discriminator) == 0) {
 		return ast_ari_validate_channel_left_bridge(json);
 	} else
@@ -4286,12 +4900,18 @@ int ast_ari_validate_message(struct ast_json *json)
 	if (strcmp("ChannelTalkingStarted", discriminator) == 0) {
 		return ast_ari_validate_channel_talking_started(json);
 	} else
+	if (strcmp("ChannelUnhold", discriminator) == 0) {
+		return ast_ari_validate_channel_unhold(json);
+	} else
 	if (strcmp("ChannelUserevent", discriminator) == 0) {
 		return ast_ari_validate_channel_userevent(json);
 	} else
 	if (strcmp("ChannelVarset", discriminator) == 0) {
 		return ast_ari_validate_channel_varset(json);
 	} else
+	if (strcmp("ContactStatusChange", discriminator) == 0) {
+		return ast_ari_validate_contact_status_change(json);
+	} else
 	if (strcmp("DeviceStateChanged", discriminator) == 0) {
 		return ast_ari_validate_device_state_changed(json);
 	} else
@@ -4307,6 +4927,9 @@ int ast_ari_validate_message(struct ast_json *json)
 	if (strcmp("MissingParams", discriminator) == 0) {
 		return ast_ari_validate_missing_params(json);
 	} else
+	if (strcmp("PeerStatusChange", discriminator) == 0) {
+		return ast_ari_validate_peer_status_change(json);
+	} else
 	if (strcmp("PlaybackFinished", discriminator) == 0) {
 		return ast_ari_validate_playback_finished(json);
 	} else
@@ -4424,6 +5047,175 @@ ari_validator ast_ari_validate_missing_params_fn(void)
 	return ast_ari_validate_missing_params;
 }
 
+int ast_ari_validate_peer(struct ast_json *json)
+{
+	int res = 1;
+	struct ast_json_iter *iter;
+	int has_peer_status = 0;
+
+	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("address", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI Peer field address failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("cause", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI Peer field cause failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("peer_status", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_peer_status = 1;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI Peer field peer_status failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("port", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI Peer field port failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("time", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI Peer field time failed validation\n");
+				res = 0;
+			}
+		} else
+		{
+			ast_log(LOG_ERROR,
+				"ARI Peer has undocumented field %s\n",
+				ast_json_object_iter_key(iter));
+			res = 0;
+		}
+	}
+
+	if (!has_peer_status) {
+		ast_log(LOG_ERROR, "ARI Peer missing required field peer_status\n");
+		res = 0;
+	}
+
+	return res;
+}
+
+ari_validator ast_ari_validate_peer_fn(void)
+{
+	return ast_ari_validate_peer;
+}
+
+int ast_ari_validate_peer_status_change(struct ast_json *json)
+{
+	int res = 1;
+	struct ast_json_iter *iter;
+	int has_type = 0;
+	int has_application = 0;
+	int has_endpoint = 0;
+	int has_peer = 0;
+
+	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_type = 1;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI PeerStatusChange field type failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("application", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_application = 1;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI PeerStatusChange field application failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_date(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI PeerStatusChange field timestamp failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("endpoint", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_endpoint = 1;
+			prop_is_valid = ast_ari_validate_endpoint(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI PeerStatusChange field endpoint failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("peer", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_peer = 1;
+			prop_is_valid = ast_ari_validate_peer(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI PeerStatusChange field peer failed validation\n");
+				res = 0;
+			}
+		} else
+		{
+			ast_log(LOG_ERROR,
+				"ARI PeerStatusChange has undocumented field %s\n",
+				ast_json_object_iter_key(iter));
+			res = 0;
+		}
+	}
+
+	if (!has_type) {
+		ast_log(LOG_ERROR, "ARI PeerStatusChange missing required field type\n");
+		res = 0;
+	}
+
+	if (!has_application) {
+		ast_log(LOG_ERROR, "ARI PeerStatusChange missing required field application\n");
+		res = 0;
+	}
+
+	if (!has_endpoint) {
+		ast_log(LOG_ERROR, "ARI PeerStatusChange missing required field endpoint\n");
+		res = 0;
+	}
+
+	if (!has_peer) {
+		ast_log(LOG_ERROR, "ARI PeerStatusChange missing required field peer\n");
+		res = 0;
+	}
+
+	return res;
+}
+
+ari_validator ast_ari_validate_peer_status_change_fn(void)
+{
+	return ast_ari_validate_peer_status_change;
+}
+
 int ast_ari_validate_playback_finished(struct ast_json *json)
 {
 	int res = 1;
diff --git a/res/ari/ari_model_validators.h b/res/ari/ari_model_validators.h
index de8547c..0bcdb0f 100644
--- a/res/ari/ari_model_validators.h
+++ b/res/ari/ari_model_validators.h
@@ -207,6 +207,60 @@ int ast_ari_validate_config_info(struct ast_json *json);
 ari_validator ast_ari_validate_config_info_fn(void);
 
 /*!
+ * \brief Validator for ConfigTuple.
+ *
+ * A key/value pair that makes up part of a configuration object.
+ *
+ * \param json JSON object to validate.
+ * \returns True (non-zero) if valid.
+ * \returns False (zero) if invalid.
+ */
+int ast_ari_validate_config_tuple(struct ast_json *json);
+
+/*!
+ * \brief Function pointer to ast_ari_validate_config_tuple().
+ *
+ * See \ref ast_ari_model_validators.h for more details.
+ */
+ari_validator ast_ari_validate_config_tuple_fn(void);
+
+/*!
+ * \brief Validator for LogChannel.
+ *
+ * Details of an Asterisk log channel
+ *
+ * \param json JSON object to validate.
+ * \returns True (non-zero) if valid.
+ * \returns False (zero) if invalid.
+ */
+int ast_ari_validate_log_channel(struct ast_json *json);
+
+/*!
+ * \brief Function pointer to ast_ari_validate_log_channel().
+ *
+ * See \ref ast_ari_model_validators.h for more details.
+ */
+ari_validator ast_ari_validate_log_channel_fn(void);
+
+/*!
+ * \brief Validator for Module.
+ *
+ * Details of an Asterisk module
+ *
+ * \param json JSON object to validate.
+ * \returns True (non-zero) if valid.
+ * \returns False (zero) if invalid.
+ */
+int ast_ari_validate_module(struct ast_json *json);
+
+/*!
+ * \brief Function pointer to ast_ari_validate_module().
+ *
+ * See \ref ast_ari_model_validators.h for more details.
+ */
+ari_validator ast_ari_validate_module_fn(void);
+
+/*!
  * \brief Validator for SetId.
  *
  * Effective user/group id
@@ -809,6 +863,24 @@ int ast_ari_validate_channel_hangup_request(struct ast_json *json);
 ari_validator ast_ari_validate_channel_hangup_request_fn(void);
 
 /*!
+ * \brief Validator for ChannelHold.
+ *
+ * A channel initiated a media hold.
+ *
+ * \param json JSON object to validate.
+ * \returns True (non-zero) if valid.
+ * \returns False (zero) if invalid.
+ */
+int ast_ari_validate_channel_hold(struct ast_json *json);
+
+/*!
+ * \brief Function pointer to ast_ari_validate_channel_hold().
+ *
+ * See \ref ast_ari_model_validators.h for more details.
+ */
+ari_validator ast_ari_validate_channel_hold_fn(void);
+
+/*!
  * \brief Validator for ChannelLeftBridge.
  *
  * Notification that a channel has left a bridge.
@@ -881,6 +953,24 @@ int ast_ari_validate_channel_talking_started(struct ast_json *json);
 ari_validator ast_ari_validate_channel_talking_started_fn(void);
 
 /*!
+ * \brief Validator for ChannelUnhold.
+ *
+ * A channel initiated a media unhold.
+ *
+ * \param json JSON object to validate.
+ * \returns True (non-zero) if valid.
+ * \returns False (zero) if invalid.
+ */
+int ast_ari_validate_channel_unhold(struct ast_json *json);
+
+/*!
+ * \brief Function pointer to ast_ari_validate_channel_unhold().
+ *
+ * See \ref ast_ari_model_validators.h for more details.
+ */
+ari_validator ast_ari_validate_channel_unhold_fn(void);
+
+/*!
  * \brief Validator for ChannelUserevent.
  *
  * User-generated event with additional user-defined fields in the object.
@@ -917,6 +1007,42 @@ int ast_ari_validate_channel_varset(struct ast_json *json);
 ari_validator ast_ari_validate_channel_varset_fn(void);
 
 /*!
+ * \brief Validator for ContactInfo.
+ *
+ * Detailed information about a contact on an endpoint.
+ *
+ * \param json JSON object to validate.
+ * \returns True (non-zero) if valid.
+ * \returns False (zero) if invalid.
+ */
+int ast_ari_validate_contact_info(struct ast_json *json);
+
+/*!
+ * \brief Function pointer to ast_ari_validate_contact_info().
+ *
+ * See \ref ast_ari_model_validators.h for more details.
+ */
+ari_validator ast_ari_validate_contact_info_fn(void);
+
+/*!
+ * \brief Validator for ContactStatusChange.
+ *
+ * The state of a contact on an endpoint has changed.
+ *
+ * \param json JSON object to validate.
+ * \returns True (non-zero) if valid.
+ * \returns False (zero) if invalid.
+ */
+int ast_ari_validate_contact_status_change(struct ast_json *json);
+
+/*!
+ * \brief Function pointer to ast_ari_validate_contact_status_change().
+ *
+ * See \ref ast_ari_model_validators.h for more details.
+ */
+ari_validator ast_ari_validate_contact_status_change_fn(void);
+
+/*!
  * \brief Validator for DeviceStateChanged.
  *
  * Notification that a device state has changed.
@@ -1025,6 +1151,42 @@ int ast_ari_validate_missing_params(struct ast_json *json);
 ari_validator ast_ari_validate_missing_params_fn(void);
 
 /*!
+ * \brief Validator for Peer.
+ *
+ * Detailed information about a remote peer that communicates with Asterisk.
+ *
+ * \param json JSON object to validate.
+ * \returns True (non-zero) if valid.
+ * \returns False (zero) if invalid.
+ */
+int ast_ari_validate_peer(struct ast_json *json);
+
+/*!
+ * \brief Function pointer to ast_ari_validate_peer().
+ *
+ * See \ref ast_ari_model_validators.h for more details.
+ */
+ari_validator ast_ari_validate_peer_fn(void);
+
+/*!
+ * \brief Validator for PeerStatusChange.
+ *
+ * The state of a peer associated with an endpoint has changed.
+ *
+ * \param json JSON object to validate.
+ * \returns True (non-zero) if valid.
+ * \returns False (zero) if invalid.
+ */
+int ast_ari_validate_peer_status_change(struct ast_json *json);
+
+/*!
+ * \brief Function pointer to ast_ari_validate_peer_status_change().
+ *
+ * See \ref ast_ari_model_validators.h for more details.
+ */
+ari_validator ast_ari_validate_peer_status_change_fn(void);
+
+/*!
  * \brief Validator for PlaybackFinished.
  *
  * Event showing the completion of a media playback operation.
@@ -1208,6 +1370,20 @@ ari_validator ast_ari_validate_application_fn(void);
  * - max_open_files: int
  * - name: string (required)
  * - setid: SetId (required)
+ * ConfigTuple
+ * - attribute: string (required)
+ * - value: string (required)
+ * LogChannel
+ * - channel: string (required)
+ * - configuration: string (required)
+ * - status: string (required)
+ * - type: string (required)
+ * Module
+ * - description: string (required)
+ * - name: string (required)
+ * - status: string (required)
+ * - support_level: string (required)
+ * - use_count: int (required)
  * SetId
  * - group: string (required)
  * - user: string (required)
@@ -1242,6 +1418,7 @@ ari_validator ast_ari_validate_application_fn(void);
  * - creationtime: Date (required)
  * - dialplan: DialplanCEP (required)
  * - id: string (required)
+ * - language: string (required)
  * - name: string (required)
  * - state: string (required)
  * Dialed
@@ -1392,6 +1569,12 @@ ari_validator ast_ari_validate_application_fn(void);
  * - cause: int
  * - channel: Channel (required)
  * - soft: boolean
+ * ChannelHold
+ * - type: string (required)
+ * - application: string (required)
+ * - timestamp: Date
+ * - channel: Channel (required)
+ * - musicclass: string
  * ChannelLeftBridge
  * - type: string (required)
  * - application: string (required)
@@ -1414,6 +1597,11 @@ ari_validator ast_ari_validate_application_fn(void);
  * - application: string (required)
  * - timestamp: Date
  * - channel: Channel (required)
+ * ChannelUnhold
+ * - type: string (required)
+ * - application: string (required)
+ * - timestamp: Date
+ * - channel: Channel (required)
  * ChannelUserevent
  * - type: string (required)
  * - application: string (required)
@@ -1430,6 +1618,17 @@ ari_validator ast_ari_validate_application_fn(void);
  * - channel: Channel
  * - value: string (required)
  * - variable: string (required)
+ * ContactInfo
+ * - aor: string (required)
+ * - contact_status: string (required)
+ * - roundtrip_usec: string
+ * - uri: string (required)
+ * ContactStatusChange
+ * - type: string (required)
+ * - application: string (required)
+ * - timestamp: Date
+ * - contact_info: ContactInfo (required)
+ * - endpoint: Endpoint (required)
  * DeviceStateChanged
  * - type: string (required)
  * - application: string (required)
@@ -1459,6 +1658,18 @@ ari_validator ast_ari_validate_application_fn(void);
  * MissingParams
  * - type: string (required)
  * - params: List[string] (required)
+ * Peer
+ * - address: string
+ * - cause: string
+ * - peer_status: string (required)
+ * - port: string
+ * - time: string
+ * PeerStatusChange
+ * - type: string (required)
+ * - application: string (required)
+ * - timestamp: Date
+ * - endpoint: Endpoint (required)
+ * - peer: Peer (required)
  * PlaybackFinished
  * - type: string (required)
  * - application: string (required)
diff --git a/res/ari/ari_websockets.c b/res/ari/ari_websockets.c
index b0052cb..8e11629 100644
--- a/res/ari/ari_websockets.c
+++ b/res/ari/ari_websockets.c
@@ -18,7 +18,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 417317 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/ari.h"
 #include "asterisk/astobj2.h"
@@ -100,6 +100,10 @@ struct ast_json *ast_ari_websocket_session_read(
 {
 	RAII_VAR(struct ast_json *, message, NULL, ast_json_unref);
 
+	if (ast_websocket_fd(session->ws_session) < 0) {
+		return NULL;
+	}
+
 	while (!message) {
 		int res;
 		char *payload;
@@ -127,7 +131,7 @@ struct ast_json *ast_ari_websocket_session_read(
 
 		switch (opcode) {
 		case AST_WEBSOCKET_OPCODE_CLOSE:
-			ast_debug(1, "WebSocket closed by peer\n");
+			ast_debug(1, "WebSocket closed\n");
 			return NULL;
 		case AST_WEBSOCKET_OPCODE_TEXT:
 			message = ast_json_load_buf(payload, payload_len, NULL);
@@ -159,9 +163,7 @@ int ast_ari_websocket_session_write(struct ast_ari_websocket_session *session,
 #ifdef AST_DEVMODE
 	if (!session->validator(message)) {
 		ast_log(LOG_ERROR, "Outgoing message failed validation\n");
-		return ast_websocket_write(session->ws_session,
-			AST_WEBSOCKET_OPCODE_TEXT, VALIDATION_FAILED,
-			strlen(VALIDATION_FAILED));
+		return ast_websocket_write_string(session->ws_session, VALIDATION_FAILED);
 	}
 #endif
 
@@ -172,9 +174,12 @@ int ast_ari_websocket_session_write(struct ast_ari_websocket_session *session,
 		return -1;
 	}
 
-	ast_debug(3, "Examining ARI event: \n%s\n", str);
-	return ast_websocket_write(session->ws_session,
-		AST_WEBSOCKET_OPCODE_TEXT, str,	strlen(str));
+	ast_debug(3, "Examining ARI event (length %u): \n%s\n", (unsigned int) strlen(str), str);
+	if (ast_websocket_write_string(session->ws_session, str)) {
+		ast_log(LOG_NOTICE, "Problem occurred during websocket write, websocket closed\n");
+		return -1;
+	}
+	return 0;
 }
 
 void ari_handle_websocket(struct ast_websocket_server *ws_server,
diff --git a/res/ari/cli.c b/res/ari/cli.c
index d18b1b8..8194079 100644
--- a/res/ari/cli.c
+++ b/res/ari/cli.c
@@ -24,7 +24,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 395603 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/astobj2.h"
 #include "asterisk/cli.h"
diff --git a/res/ari/config.c b/res/ari/config.c
index e158a93..2d3a80d 100644
--- a/res/ari/config.c
+++ b/res/ari/config.c
@@ -24,7 +24,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 417317 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/config_options.h"
 #include "asterisk/http_websocket.h"
@@ -116,19 +116,31 @@ static void *user_alloc(const char *cat)
 static int user_sort_cmp(const void *obj_left, const void *obj_right, int flags)
 {
 	const struct ast_ari_conf_user *user_left = obj_left;
-
-	if (flags & OBJ_PARTIAL_KEY) {
-		const char *key_right = obj_right;
-		return strncasecmp(user_left->username, key_right,
-			strlen(key_right));
-	} else if (flags & OBJ_KEY) {
-		const char *key_right = obj_right;
-		return strcasecmp(user_left->username, key_right);
-	} else {
-		const struct ast_ari_conf_user *user_right = obj_right;
-		const char *key_right = user_right->username;
-		return strcasecmp(user_left->username, key_right);
+	const struct ast_ari_conf_user *user_right = obj_right;
+	const char *key_right = obj_right;
+	int cmp;
+
+	switch (flags & OBJ_SEARCH_MASK) {
+	case OBJ_SEARCH_OBJECT:
+		key_right = user_right->username;
+		/* Fall through */
+	case OBJ_SEARCH_KEY:
+		cmp = strcasecmp(user_left->username, key_right);
+		break;
+	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.
+		 */
+		cmp = strncasecmp(user_left->username, key_right, strlen(key_right));
+		break;
+	default:
+		/* Sort can only work on something with a full or partial key. */
+		ast_assert(0);
+		cmp = 0;
+		break;
 	}
+	return cmp;
 }
 
 /*! \brief \ref aco_type item_find function */
@@ -138,7 +150,7 @@ static void *user_find(struct ao2_container *tmp_container, const char *cat)
 		return NULL;
 	}
 
-	return ao2_find(tmp_container, cat, OBJ_KEY);
+	return ao2_find(tmp_container, cat, OBJ_SEARCH_KEY);
 }
 
 static struct aco_type user_option = {
@@ -155,13 +167,18 @@ static struct aco_type user_option = {
 
 static struct aco_type *user[] = ACO_TYPES(&user_option);
 
+static void conf_general_dtor(void *obj)
+{
+	struct ast_ari_conf_general *general = obj;
+
+	ast_string_field_free_memory(general);
+}
+
 /*! \brief \ref ast_ari_conf destructor. */
 static void conf_destructor(void *obj)
 {
 	struct ast_ari_conf *cfg = obj;
 
-	ast_string_field_free_memory(cfg->general);
-
 	ao2_cleanup(cfg->general);
 	ao2_cleanup(cfg->users);
 }
@@ -169,7 +186,7 @@ static void conf_destructor(void *obj)
 /*! \brief Allocate an \ref ast_ari_conf for config parsing */
 static void *conf_alloc(void)
 {
-	RAII_VAR(struct ast_ari_conf *, cfg, NULL, ao2_cleanup);
+	struct ast_ari_conf *cfg;
 
 	cfg = ao2_alloc_options(sizeof(*cfg), conf_destructor,
 		AO2_ALLOC_OPT_LOCK_NOLOCK);
@@ -177,21 +194,20 @@ static void *conf_alloc(void)
 		return NULL;
 	}
 
-	cfg->general = ao2_alloc_options(sizeof(*cfg->general), NULL,
+	cfg->general = ao2_alloc_options(sizeof(*cfg->general), conf_general_dtor,
 		AO2_ALLOC_OPT_LOCK_NOLOCK);
-	if (!cfg->general) {
-		return NULL;
-	}
-	aco_set_defaults(&general_option, "general", cfg->general);
-
-	if (ast_string_field_init(cfg->general, 64)) {
-		return NULL;
-	}
 
 	cfg->users = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK,
 		AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, user_sort_cmp, NULL);
 
-	ao2_ref(cfg, +1);
+	if (!cfg->users
+		|| !cfg->general
+		|| ast_string_field_init(cfg->general, 64)
+		|| aco_set_defaults(&general_option, "general", cfg->general)) {
+		ao2_ref(cfg, -1);
+		return NULL;
+	}
+
 	return cfg;
 }
 
@@ -230,7 +246,7 @@ struct ast_ari_conf_user *ast_ari_config_validate_user(const char *username,
 		return NULL;
 	}
 
-	user = ao2_find(conf->users, username, OBJ_KEY);
+	user = ao2_find(conf->users, username, OBJ_SEARCH_KEY);
 	if (!user) {
 		return NULL;
 	}
@@ -309,6 +325,7 @@ int ast_ari_config_init(void)
 		return -1;
 	}
 
+	/* ARI general category options */
 	aco_option_register(&cfg_info, "enabled", ACO_EXACT, general_options,
 		"yes", OPT_BOOL_T, 1,
 		FLDSET(struct ast_ari_conf_general, enabled));
@@ -325,6 +342,7 @@ int ast_ari_config_init(void)
 		AST_DEFAULT_WEBSOCKET_WRITE_TIMEOUT_STR, OPT_INT_T, PARSE_IN_RANGE,
 		FLDSET(struct ast_ari_conf_general, write_timeout), 1, INT_MAX);
 
+	/* ARI type=user category options */
 	aco_option_register(&cfg_info, "type", ACO_EXACT, user, NULL,
 		OPT_NOOP_T, 0, 0);
 	aco_option_register(&cfg_info, "read_only", ACO_EXACT, user,
diff --git a/res/ari/resource_applications.c b/res/ari/resource_applications.c
index 2cf7ac5..85a631a 100644
--- a/res/ari/resource_applications.c
+++ b/res/ari/resource_applications.c
@@ -26,7 +26,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 405326 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/stasis_app.h"
 #include "resource_applications.h"
diff --git a/res/ari/resource_asterisk.c b/res/ari/resource_asterisk.c
index 0466e64..30684d2 100644
--- a/res/ari/resource_asterisk.c
+++ b/res/ari/resource_asterisk.c
@@ -29,14 +29,270 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 402529 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/ast_version.h"
 #include "asterisk/buildinfo.h"
+#include "asterisk/logger.h"
+#include "asterisk/module.h"
 #include "asterisk/paths.h"
 #include "asterisk/pbx.h"
+#include "asterisk/sorcery.h"
 #include "resource_asterisk.h"
 
+static void return_sorcery_object(struct ast_sorcery *sorcery, void *sorcery_obj,
+	struct ast_ari_response *response)
+{
+	RAII_VAR(struct ast_json *, return_set, NULL, ast_json_unref);
+	struct ast_variable *change_set;
+	struct ast_variable *it_change_set;
+
+	return_set = ast_json_array_create();
+	if (!return_set) {
+		ast_ari_response_alloc_failed(response);
+		return;
+	}
+
+	/* Note that we can't use the sorcery JSON change set directly,
+	 * as it will hand us back an Object (with fields), and we need
+	 * a more generic representation of whatever the API call asked
+	 * for, i.e., a list of tuples.
+	 */
+	change_set = ast_sorcery_objectset_create(sorcery, sorcery_obj);
+	if (!change_set) {
+		ast_ari_response_alloc_failed(response);
+		return;
+	}
+
+	for (it_change_set = change_set; it_change_set; it_change_set = it_change_set->next) {
+		struct ast_json *tuple;
+
+		tuple = ast_json_pack("{s: s, s: s}",
+			"attribute", it_change_set->name,
+			"value", it_change_set->value);
+		if (!tuple) {
+			ast_variables_destroy(change_set);
+			ast_ari_response_alloc_failed(response);
+			return;
+		}
+
+		if (ast_json_array_append(return_set, tuple)) {
+			ast_json_unref(tuple);
+			ast_variables_destroy(change_set);
+			ast_ari_response_alloc_failed(response);
+			return;
+		}
+	}
+	ast_variables_destroy(change_set);
+
+	ast_ari_response_ok(response, ast_json_ref(return_set));
+}
+
+void ast_ari_asterisk_get_object(struct ast_variable *headers,
+	struct ast_ari_asterisk_get_object_args *args,
+	struct ast_ari_response *response)
+{
+	RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
+	RAII_VAR(struct ast_sorcery_object_type *, object_type, NULL, ao2_cleanup);
+	RAII_VAR(void *, sorcery_obj, NULL, ao2_cleanup);
+
+
+	sorcery = ast_sorcery_retrieve_by_module_name(args->config_class);
+	if (!sorcery) {
+		ast_ari_response_error(
+			response, 404, "Not Found",
+			"configClass '%s' not found",
+			args->config_class);
+		return;
+	}
+
+	object_type = ast_sorcery_get_object_type(sorcery, args->object_type);
+	if (!object_type) {
+		ast_ari_response_error(
+			response, 404, "Not Found",
+			"objectType '%s' not found",
+			args->object_type);
+		return;
+	}
+
+	sorcery_obj = ast_sorcery_retrieve_by_id(sorcery, args->object_type, args->id);
+	if (!sorcery_obj) {
+		ast_ari_response_error(
+			response, 404, "Not Found",
+			"Object with id '%s' not found",
+			args->id);
+		return;
+	}
+
+	return_sorcery_object(sorcery, sorcery_obj, response);
+}
+
+void ast_ari_asterisk_update_object(struct ast_variable *headers, struct ast_ari_asterisk_update_object_args *args, struct ast_ari_response *response)
+{
+	RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
+	RAII_VAR(struct ast_sorcery_object_type *, object_type, NULL, ao2_cleanup);
+	RAII_VAR(void *, sorcery_obj, NULL, ao2_cleanup);
+	struct ast_json *fields;
+	struct ast_variable *update_set = NULL;
+	int created = 0;
+
+	sorcery = ast_sorcery_retrieve_by_module_name(args->config_class);
+	if (!sorcery) {
+		ast_ari_response_error(
+			response, 404, "Not Found",
+			"configClass '%s' not found",
+			args->config_class);
+		return;
+	}
+
+	object_type = ast_sorcery_get_object_type(sorcery, args->object_type);
+	if (!object_type) {
+		ast_ari_response_error(
+			response, 404, "Not Found",
+			"objectType '%s' not found",
+			args->object_type);
+		return;
+	}
+
+	sorcery_obj = ast_sorcery_retrieve_by_id(sorcery, args->object_type, args->id);
+	if (!sorcery_obj) {
+		ast_debug(5, "Sorcery object '%s' does not exist; creating it\n", args->id);
+		sorcery_obj = ast_sorcery_alloc(sorcery, args->object_type, args->id);
+		if (!sorcery_obj) {
+			ast_ari_response_alloc_failed(response);
+			return;
+		}
+
+		created = 1;
+	} else {
+		void *copy;
+
+		copy = ast_sorcery_copy(sorcery, sorcery_obj);
+		if (!copy) {
+			ast_ari_response_alloc_failed(response);
+			return;
+		}
+
+		ao2_ref(sorcery_obj, -1);
+		sorcery_obj = copy;
+	}
+
+	fields = ast_json_object_get(args->fields, "fields");
+	if (!fields && !created) {
+		/* Whoops. We need data. */
+		ast_ari_response_error(
+			response, 400, "Bad request",
+			"Fields must be provided to update object '%s'",
+			args->id);
+		return;
+	} else if (fields) {
+		size_t i;
+
+		for (i = 0; i < ast_json_array_size(fields); i++) {
+			struct ast_variable *new_var;
+			struct ast_json *json_value = ast_json_array_get(fields, i);
+
+			if (!json_value) {
+				continue;
+			}
+
+			new_var = ast_variable_new(
+				ast_json_string_get(ast_json_object_get(json_value, "attribute")),
+				ast_json_string_get(ast_json_object_get(json_value, "value")),
+				"");
+			if (!new_var) {
+				ast_variables_destroy(update_set);
+				ast_ari_response_alloc_failed(response);
+				return;
+			}
+			ast_variable_list_append(&update_set, new_var);
+		}
+	}
+
+	/* APPLY! Note that a NULL update_set is fine (and necessary), as it
+	 * will force validation on a newly created object.
+	 */
+	if (ast_sorcery_objectset_apply(sorcery, sorcery_obj, update_set)) {
+		ast_variables_destroy(update_set);
+		ast_ari_response_error(
+			response, 400, "Bad request",
+			"%s of object '%s' failed field value validation",
+			created ? "Creation" : "Update",
+			args->id);
+		return;
+	}
+
+	ast_variables_destroy(update_set);
+
+	if (created) {
+		if (ast_sorcery_create(sorcery, sorcery_obj)) {
+			ast_ari_response_error(
+				response, 403, "Forbidden",
+				"Cannot create sorcery objects of type '%s'",
+				args->object_type);
+			return;
+		}
+	} else {
+		if (ast_sorcery_update(sorcery, sorcery_obj)) {
+			ast_ari_response_error(
+				response, 403, "Forbidden",
+				"Cannot update sorcery objects of type '%s'",
+				args->object_type);
+			return;
+		}
+	}
+
+	return_sorcery_object(sorcery, sorcery_obj, response);
+}
+
+
+void ast_ari_asterisk_delete_object(struct ast_variable *headers,
+	struct ast_ari_asterisk_delete_object_args *args,
+	struct ast_ari_response *response)
+{
+	RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
+	RAII_VAR(struct ast_sorcery_object_type *, object_type, NULL, ao2_cleanup);
+	RAII_VAR(void *, sorcery_obj, NULL, ao2_cleanup);
+
+	sorcery = ast_sorcery_retrieve_by_module_name(args->config_class);
+	if (!sorcery) {
+		ast_ari_response_error(
+			response, 404, "Not Found",
+			"configClass '%s' not found",
+			args->config_class);
+		return;
+	}
+
+	object_type = ast_sorcery_get_object_type(sorcery, args->object_type);
+	if (!object_type) {
+		ast_ari_response_error(
+			response, 404, "Not Found",
+			"objectType '%s' not found",
+			args->object_type);
+		return;
+	}
+
+	sorcery_obj = ast_sorcery_retrieve_by_id(sorcery, args->object_type, args->id);
+	if (!sorcery_obj) {
+		ast_ari_response_error(
+			response, 404, "Not Found",
+			"Object with id '%s' not found",
+			args->id);
+		return;
+	}
+
+	if (ast_sorcery_delete(sorcery, sorcery_obj)) {
+		ast_ari_response_error(
+			response, 403, "Forbidden",
+			"Could not delete object with id '%s'",
+			args->id);
+		return;
+	}
+
+	ast_ari_response_no_content(response);
+}
+
+
 void ast_ari_asterisk_get_info(struct ast_variable *headers,
 	struct ast_ari_asterisk_get_info_args *args,
 	struct ast_ari_response *response)
@@ -140,6 +396,364 @@ void ast_ari_asterisk_get_info(struct ast_variable *headers,
 	ast_ari_response_ok(response, ast_json_ref(json));
 }
 
+/*!
+ * \brief Process module information and append to a json array
+ * \param module Resource name
+ * \param description Resource description
+ * \param usecnt Resource use count
+ * \param status Resource running status
+ * \param like
+ * \param support_level Resource support level
+ * \param module_data_list Resource array
+ *
+ * \retval 0 if no resource exists
+ * \retval 1 if resource exists
+ */
+static int process_module_list(const char *module, const char *description, int usecnt,
+                               const char *status, const char *like,
+                               enum ast_module_support_level support_level, void *module_data_list)
+{
+	struct ast_json *module_info;
+
+	module_info = ast_json_pack("{s: s, s: s, s: i, s: s, s: s}",
+                              "name", module,
+                              "description", description,
+                              "use_count", usecnt,
+                              "status", status,
+                              "support_level", ast_module_support_level_to_string(support_level));
+	if (!module_info) {
+		return 0;
+	}
+	ast_json_array_append(module_data_list, module_info);
+	return 1;
+}
+
+void ast_ari_asterisk_list_modules(struct ast_variable *headers,
+	struct ast_ari_asterisk_list_modules_args *args,
+	struct ast_ari_response *response)
+{
+	struct ast_json *json;
+
+	json = ast_json_array_create();
+	ast_update_module_list_data(&process_module_list, NULL, json);
+
+	ast_ari_response_ok(response, json);
+}
+
+/*!
+ * \brief Identify module by name and process resource information
+ * \param module Resource name
+ * \param description Resource description
+ * \param usecnt Resource use count
+ * \param status Resource running status
+ * \param like
+ * \param support_level Resource support level
+ * \param data JSON body for resource
+ * \param condition Name to match resource to
+ *
+ * \retval 0 if no resource exists
+ * \retval 1 if resource exists
+ */
+static int identify_module(const char *module, const char *description, int usecnt,
+                           const char *status, const char *like,
+                           enum ast_module_support_level support_level, void *data,
+                           const char *condition)
+{
+	int json_obj_set = 0;
+
+	if (strcmp(condition, module) != 0) {
+		return 0;
+	}
+
+	json_obj_set += ast_json_object_set(data, "name", ast_json_string_create(module));
+	json_obj_set += ast_json_object_set(data, "description", ast_json_string_create(description));
+	json_obj_set += ast_json_object_set(data, "use_count", ast_json_integer_create(usecnt));
+	json_obj_set += ast_json_object_set(data, "status", ast_json_string_create(status));
+	json_obj_set += ast_json_object_set(data, "support_level", ast_json_string_create(
+	                                    ast_module_support_level_to_string(support_level)));
+
+	if (json_obj_set != 0) {
+		return 0;
+	}
+
+	return 1;
+}
+
+void ast_ari_asterisk_get_module(struct ast_variable *headers,
+	struct ast_ari_asterisk_get_module_args *args,
+	struct ast_ari_response *response)
+{
+	struct ast_json *json;
+	int module_retrieved = 0;
+
+	ast_assert(response != NULL);
+
+	if (!ast_module_check(args->module_name)) {
+		ast_ari_response_error(
+			response, 404, "Not Found",
+			"Module could not be found in running modules");
+		return;
+	}
+
+	json = ast_json_object_create();
+	if (!json) {
+		ast_ari_response_alloc_failed(response);
+		return;
+	}
+
+	module_retrieved = ast_update_module_list_condition(&identify_module, NULL, json,
+	                                                    args->module_name);
+	if (!module_retrieved) {
+		ast_ari_response_error(
+			response, 409, "Conflict",
+			"Module information could not be retrieved");
+		return;
+	}
+
+	ast_ari_response_ok(response, json);
+}
+
+void ast_ari_asterisk_load_module(struct ast_variable *headers,
+	struct ast_ari_asterisk_load_module_args *args,
+	struct ast_ari_response *response)
+{
+	enum ast_module_load_result load_result;
+
+	ast_assert(response != NULL);
+
+	if (ast_module_check(args->module_name)) {
+		ast_ari_response_error(
+			response, 409, "Conflict",
+			"Module is already loaded");
+		return;
+	}
+
+	load_result = ast_load_resource(args->module_name);
+
+	if (load_result == AST_MODULE_LOAD_DECLINE) {
+		ast_ari_response_error(
+			response, 409, "Conflict",
+			"Module load declined");
+		return;
+	} else if (load_result == AST_MODULE_LOAD_SKIP) {
+		ast_ari_response_error(
+			response, 409, "Conflict",
+			"Module was skipped");
+		return;
+	} else if (load_result == AST_MODULE_LOAD_FAILURE) {
+		ast_ari_response_error(
+			response, 409, "Conflict",
+			"Module could not be loaded properly");
+		return;
+	}
+
+	ast_ari_response_no_content(response);
+}
+
+void ast_ari_asterisk_unload_module(struct ast_variable *headers,
+	struct ast_ari_asterisk_unload_module_args *args,
+	struct ast_ari_response *response)
+{
+	int unload_result;
+	enum ast_module_unload_mode unload_mode = AST_FORCE_SOFT;
+
+	ast_assert(response != NULL);
+
+	if (!ast_module_check(args->module_name)) {
+		ast_ari_response_error(
+			response, 404, "Not Found",
+			"Module not found in running modules");
+		return;
+	}
+
+	unload_result = ast_unload_resource(args->module_name, unload_mode);
+
+	if (unload_result != 0) {
+		ast_ari_response_error(
+			response, 409, "Conflict",
+			"Module could not be unloaded");
+		return;
+	}
+
+	ast_ari_response_no_content(response);
+}
+
+void ast_ari_asterisk_reload_module(struct ast_variable *headers,
+	struct ast_ari_asterisk_reload_module_args *args,
+	struct ast_ari_response *response)
+{
+	enum ast_module_reload_result reload_result;
+
+	ast_assert(response != NULL);
+
+	if (!ast_module_check(args->module_name)) {
+		ast_ari_response_error(
+			response, 404, "Not Found",
+			"Module not found in running modules");
+		return;
+	}
+
+	reload_result = ast_module_reload(args->module_name);
+
+	if (reload_result == AST_MODULE_RELOAD_NOT_FOUND) {
+		ast_ari_response_error(
+			response, 404, "Not Found",
+			"Module could not be found");
+		return;
+	} else if (reload_result == AST_MODULE_RELOAD_ERROR) {
+		ast_ari_response_error(
+			response, 409, "Conflict",
+			"An unknown error occurred while reloading the module");
+		return;
+	} else if (reload_result == AST_MODULE_RELOAD_IN_PROGRESS) {
+		ast_ari_response_error(
+			response, 409, "Conflict",
+			"Another reload is currently in progress");
+		return;
+	} else if (reload_result == AST_MODULE_RELOAD_UNINITIALIZED) {
+		ast_ari_response_error(
+			response, 409, "Conflict",
+			"Module has not been initialized");
+		return;
+	} else if (reload_result == AST_MODULE_RELOAD_NOT_IMPLEMENTED) {
+		ast_ari_response_error(
+			response, 409, "Conflict",
+			"Module does not support reloading");
+		return;
+	} else if (reload_result == AST_MODULE_RELOAD_QUEUED) {
+		ast_ari_response_accepted(response);
+		return;
+	}
+
+	ast_ari_response_no_content(response);
+}
+
+/*!
+ * \brief Process logger information and append to a json array
+ * \param channel Resource logger channel name path
+ * \param type Resource log type
+ * \param status Resource log status
+ * \param configuration Resource logger levels
+ * \param log_data_list Resource array
+ *
+ * \retval -1 if no resource exists
+ * \retval 0 if resource exists
+ */
+static int process_log_list(const char *channel, const char *type,
+	const char *status, const char *configuration, void *log_data_list)
+{
+	struct ast_json *logger_info;
+
+	logger_info = ast_json_pack("{s: s, s: s, s: s, s: s}",
+		"channel", channel, "type", type, "status", status, "configuration",
+		configuration);
+
+	if (!logger_info) {
+		return AST_LOGGER_FAILURE;
+	}
+
+	ast_json_array_append(log_data_list, logger_info);
+	return AST_LOGGER_SUCCESS;
+}
+
+void ast_ari_asterisk_list_log_channels(struct ast_variable *headers,
+	struct ast_ari_asterisk_list_log_channels_args *args,
+	struct ast_ari_response *response)
+{
+	struct ast_json *json;
+	int res;
+
+	json = ast_json_array_create();
+	res = ast_logger_get_channels(&process_log_list, json);
+
+	if (res == AST_LOGGER_FAILURE) {
+		ast_ari_response_error(response, 500, "Internal Server Error",
+			"Response body is not valid");
+		return;
+	} else if (res == AST_LOGGER_ALLOC_ERROR) {
+		ast_ari_response_error(response, 500, "Internal Server Error",
+			"Allocation Failed");
+		return;
+	}
+
+	ast_ari_response_ok(response, json);
+}
+
+void ast_ari_asterisk_add_log(struct ast_variable *headers,
+	struct ast_ari_asterisk_add_log_args *args,
+	struct ast_ari_response *response)
+{
+	int res;
+
+	ast_assert(response != NULL);
+
+	res = ast_logger_create_channel(args->log_channel_name, args->configuration);
+
+	if (res == AST_LOGGER_DECLINE) {
+		ast_ari_response_error(response, 400, "Bad Request",
+			"Configuration levels are required");
+		return;
+	} else if (res == AST_LOGGER_FAILURE) {
+		ast_ari_response_error(response, 409, "Conflict",
+			"Log channel already exists");
+		return;
+	} else if (res == AST_LOGGER_ALLOC_ERROR) {
+		ast_ari_response_error(response, 500, "Internal Server Error",
+			"Allocation failed");
+		return;
+	}
+
+	ast_ari_response_no_content(response);
+}
+
+void ast_ari_asterisk_rotate_log(struct ast_variable *headers,
+	struct ast_ari_asterisk_rotate_log_args *args,
+	struct ast_ari_response *response)
+{
+	int res;
+
+	ast_assert(response != NULL);
+
+	res = ast_logger_rotate_channel(args->log_channel_name);
+
+	if (res == AST_LOGGER_FAILURE) {
+		ast_ari_response_error(
+			response, 404, "Not Found",
+			"Log channel does not exist");
+		return;
+	} else if (res == AST_LOGGER_ALLOC_ERROR) {
+		ast_ari_response_error(
+			response, 500, "Internal Server Error",
+			"Allocation failed");
+		return;
+	}
+
+	ast_ari_response_no_content(response);
+}
+
+void ast_ari_asterisk_delete_log(struct ast_variable *headers,
+	struct ast_ari_asterisk_delete_log_args *args,
+	struct ast_ari_response *response)
+{
+	int res;
+
+	ast_assert(response != NULL);
+
+	res = ast_logger_remove_channel(args->log_channel_name);
+
+	if (res == AST_LOGGER_FAILURE) {
+		ast_ari_response_error(response, 404, "Not Found",
+			"Log channel does not exist");
+		return;
+	} else if (res == AST_LOGGER_ALLOC_ERROR) {
+		ast_ari_response_error(response, 500, "Internal Server Error",
+			"Allocation failed");
+		return;
+	}
+
+	ast_ari_response_no_content(response);
+}
+
 void ast_ari_asterisk_get_global_var(struct ast_variable *headers,
 	struct ast_ari_asterisk_get_global_var_args *args,
 	struct ast_ari_response *response)
diff --git a/res/ari/resource_asterisk.h b/res/ari/resource_asterisk.h
index dc4b183..a4a7da0 100644
--- a/res/ari/resource_asterisk.h
+++ b/res/ari/resource_asterisk.h
@@ -39,6 +39,70 @@
 
 #include "asterisk/ari.h"
 
+/*! Argument struct for ast_ari_asterisk_get_object() */
+struct ast_ari_asterisk_get_object_args {
+	/*! The configuration class containing dynamic configuration objects. */
+	const char *config_class;
+	/*! The type of configuration object to retrieve. */
+	const char *object_type;
+	/*! The unique identifier of the object to retrieve. */
+	const char *id;
+};
+/*!
+ * \brief Retrieve a dynamic configuration object.
+ *
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ * \param[out] response HTTP response
+ */
+void ast_ari_asterisk_get_object(struct ast_variable *headers, struct ast_ari_asterisk_get_object_args *args, struct ast_ari_response *response);
+/*! Argument struct for ast_ari_asterisk_update_object() */
+struct ast_ari_asterisk_update_object_args {
+	/*! The configuration class containing dynamic configuration objects. */
+	const char *config_class;
+	/*! The type of configuration object to create or update. */
+	const char *object_type;
+	/*! The unique identifier of the object to create or update. */
+	const char *id;
+	/*! The body object should have a value that is a list of ConfigTuples, which provide the fields to update. Ex. [ { "attribute": "directmedia", "value": "false" } ] */
+	struct ast_json *fields;
+};
+/*!
+ * \brief Body parsing function for /asterisk/config/dynamic/{configClass}/{objectType}/{id}.
+ * \param body The JSON body from which to parse parameters.
+ * \param[out] args The args structure to parse into.
+ * \retval zero on success
+ * \retval non-zero on failure
+ */
+int ast_ari_asterisk_update_object_parse_body(
+	struct ast_json *body,
+	struct ast_ari_asterisk_update_object_args *args);
+
+/*!
+ * \brief Create or update a dynamic configuration object.
+ *
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ * \param[out] response HTTP response
+ */
+void ast_ari_asterisk_update_object(struct ast_variable *headers, struct ast_ari_asterisk_update_object_args *args, struct ast_ari_response *response);
+/*! Argument struct for ast_ari_asterisk_delete_object() */
+struct ast_ari_asterisk_delete_object_args {
+	/*! The configuration class containing dynamic configuration objects. */
+	const char *config_class;
+	/*! The type of configuration object to delete. */
+	const char *object_type;
+	/*! The unique identifier of the object to delete. */
+	const char *id;
+};
+/*!
+ * \brief Delete a dynamic configuration object.
+ *
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ * \param[out] response HTTP response
+ */
+void ast_ari_asterisk_delete_object(struct ast_variable *headers, struct ast_ari_asterisk_delete_object_args *args, struct ast_ari_response *response);
 /*! Argument struct for ast_ari_asterisk_get_info() */
 struct ast_ari_asterisk_get_info_args {
 	/*! Array of Filter information returned */
@@ -67,6 +131,132 @@ int ast_ari_asterisk_get_info_parse_body(
  * \param[out] response HTTP response
  */
 void ast_ari_asterisk_get_info(struct ast_variable *headers, struct ast_ari_asterisk_get_info_args *args, struct ast_ari_response *response);
+/*! Argument struct for ast_ari_asterisk_list_modules() */
+struct ast_ari_asterisk_list_modules_args {
+};
+/*!
+ * \brief List Asterisk modules.
+ *
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ * \param[out] response HTTP response
+ */
+void ast_ari_asterisk_list_modules(struct ast_variable *headers, struct ast_ari_asterisk_list_modules_args *args, struct ast_ari_response *response);
+/*! Argument struct for ast_ari_asterisk_get_module() */
+struct ast_ari_asterisk_get_module_args {
+	/*! Module's name */
+	const char *module_name;
+};
+/*!
+ * \brief Get Asterisk module information.
+ *
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ * \param[out] response HTTP response
+ */
+void ast_ari_asterisk_get_module(struct ast_variable *headers, struct ast_ari_asterisk_get_module_args *args, struct ast_ari_response *response);
+/*! Argument struct for ast_ari_asterisk_load_module() */
+struct ast_ari_asterisk_load_module_args {
+	/*! Module's name */
+	const char *module_name;
+};
+/*!
+ * \brief Load an Asterisk module.
+ *
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ * \param[out] response HTTP response
+ */
+void ast_ari_asterisk_load_module(struct ast_variable *headers, struct ast_ari_asterisk_load_module_args *args, struct ast_ari_response *response);
+/*! Argument struct for ast_ari_asterisk_unload_module() */
+struct ast_ari_asterisk_unload_module_args {
+	/*! Module's name */
+	const char *module_name;
+};
+/*!
+ * \brief Unload an Asterisk module.
+ *
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ * \param[out] response HTTP response
+ */
+void ast_ari_asterisk_unload_module(struct ast_variable *headers, struct ast_ari_asterisk_unload_module_args *args, struct ast_ari_response *response);
+/*! Argument struct for ast_ari_asterisk_reload_module() */
+struct ast_ari_asterisk_reload_module_args {
+	/*! Module's name */
+	const char *module_name;
+};
+/*!
+ * \brief Reload an Asterisk module.
+ *
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ * \param[out] response HTTP response
+ */
+void ast_ari_asterisk_reload_module(struct ast_variable *headers, struct ast_ari_asterisk_reload_module_args *args, struct ast_ari_response *response);
+/*! Argument struct for ast_ari_asterisk_list_log_channels() */
+struct ast_ari_asterisk_list_log_channels_args {
+};
+/*!
+ * \brief Gets Asterisk log channel information.
+ *
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ * \param[out] response HTTP response
+ */
+void ast_ari_asterisk_list_log_channels(struct ast_variable *headers, struct ast_ari_asterisk_list_log_channels_args *args, struct ast_ari_response *response);
+/*! Argument struct for ast_ari_asterisk_add_log() */
+struct ast_ari_asterisk_add_log_args {
+	/*! The log channel to add */
+	const char *log_channel_name;
+	/*! levels of the log channel */
+	const char *configuration;
+};
+/*!
+ * \brief Body parsing function for /asterisk/logging/{logChannelName}.
+ * \param body The JSON body from which to parse parameters.
+ * \param[out] args The args structure to parse into.
+ * \retval zero on success
+ * \retval non-zero on failure
+ */
+int ast_ari_asterisk_add_log_parse_body(
+	struct ast_json *body,
+	struct ast_ari_asterisk_add_log_args *args);
+
+/*!
+ * \brief Adds a log channel.
+ *
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ * \param[out] response HTTP response
+ */
+void ast_ari_asterisk_add_log(struct ast_variable *headers, struct ast_ari_asterisk_add_log_args *args, struct ast_ari_response *response);
+/*! Argument struct for ast_ari_asterisk_delete_log() */
+struct ast_ari_asterisk_delete_log_args {
+	/*! Log channels name */
+	const char *log_channel_name;
+};
+/*!
+ * \brief Deletes a log channel.
+ *
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ * \param[out] response HTTP response
+ */
+void ast_ari_asterisk_delete_log(struct ast_variable *headers, struct ast_ari_asterisk_delete_log_args *args, struct ast_ari_response *response);
+/*! Argument struct for ast_ari_asterisk_rotate_log() */
+struct ast_ari_asterisk_rotate_log_args {
+	/*! Log channel's name */
+	const char *log_channel_name;
+};
+/*!
+ * \brief Rotates a log channel.
+ *
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ * \param[out] response HTTP response
+ */
+void ast_ari_asterisk_rotate_log(struct ast_variable *headers, struct ast_ari_asterisk_rotate_log_args *args, struct ast_ari_response *response);
 /*! Argument struct for ast_ari_asterisk_get_global_var() */
 struct ast_ari_asterisk_get_global_var_args {
 	/*! The variable to get */
diff --git a/res/ari/resource_bridges.c b/res/ari/resource_bridges.c
index 3283dec..ad37053 100644
--- a/res/ari/resource_bridges.c
+++ b/res/ari/resource_bridges.c
@@ -24,12 +24,14 @@
  */
 
 /*** MODULEINFO
+	<depend type="module">res_stasis_recording</depend>
+	<depend type="module">res_stasis_playback</depend>
 	<support_level>core</support_level>
  ***/
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420796 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "resource_bridges.h"
 #include "asterisk/stasis.h"
@@ -693,7 +695,7 @@ void ast_ari_bridges_record(struct ast_variable *headers,
 		return;
 	}
 
-	if (options->if_exists == -1) {
+	if (options->if_exists == AST_RECORD_IF_EXISTS_ERROR) {
 		ast_ari_response_error(
 			response, 400, "Bad Request",
 			"ifExists invalid");
@@ -940,8 +942,8 @@ void ast_ari_bridges_create(struct ast_variable *headers,
 		ast_bridge_snapshot_to_json(snapshot, stasis_app_get_sanitizer()));
 }
 
-void ast_ari_bridges_create_or_update_with_id(struct ast_variable *headers,
-	struct ast_ari_bridges_create_or_update_with_id_args *args,
+void ast_ari_bridges_create_with_id(struct ast_variable *headers,
+	struct ast_ari_bridges_create_with_id_args *args,
 	struct ast_ari_response *response)
 {
 	RAII_VAR(struct ast_bridge *, bridge, find_bridge(response, args->bridge_id), ao2_cleanup);
@@ -949,16 +951,18 @@ void ast_ari_bridges_create_or_update_with_id(struct ast_variable *headers,
 
 	if (bridge) {
 		/* update */
-		if (strcmp(args->name, bridge->name)) {
-			ast_ari_response_error(
-				response, 500, "Internal Error",
-				"Changing bridge name is not implemented");
-			return;
+		if (!ast_strlen_zero(args->name)) {
+			if (!strcmp(args->name, bridge->name)) {
+				ast_ari_response_error(
+					response, 500, "Internal Error",
+					"Changing bridge name is not implemented");
+				return;
+			}
 		}
 		if (!ast_strlen_zero(args->type)) {
 			ast_ari_response_error(
 				response, 500, "Internal Error",
-				"Changing bridge type is not implemented");
+				"Supplying a bridge type when updating a bridge is not allowed.");
 			return;
 		}
 		ast_ari_response_ok(response,
diff --git a/res/ari/resource_bridges.h b/res/ari/resource_bridges.h
index 2b1e787..36ff6a0 100644
--- a/res/ari/resource_bridges.h
+++ b/res/ari/resource_bridges.h
@@ -80,8 +80,8 @@ int ast_ari_bridges_create_parse_body(
  * \param[out] response HTTP response
  */
 void ast_ari_bridges_create(struct ast_variable *headers, struct ast_ari_bridges_create_args *args, struct ast_ari_response *response);
-/*! Argument struct for ast_ari_bridges_create_or_update_with_id() */
-struct ast_ari_bridges_create_or_update_with_id_args {
+/*! Argument struct for ast_ari_bridges_create_with_id() */
+struct ast_ari_bridges_create_with_id_args {
 	/*! Comma separated list of bridge type attributes (mixing, holding, dtmf_events, proxy_media) to set. */
 	const char *type;
 	/*! Unique ID to give to the bridge being created. */
@@ -96,9 +96,9 @@ struct ast_ari_bridges_create_or_update_with_id_args {
  * \retval zero on success
  * \retval non-zero on failure
  */
-int ast_ari_bridges_create_or_update_with_id_parse_body(
+int ast_ari_bridges_create_with_id_parse_body(
 	struct ast_json *body,
-	struct ast_ari_bridges_create_or_update_with_id_args *args);
+	struct ast_ari_bridges_create_with_id_args *args);
 
 /*!
  * \brief Create a new bridge or updates an existing one.
@@ -109,7 +109,7 @@ int ast_ari_bridges_create_or_update_with_id_parse_body(
  * \param args Swagger parameters
  * \param[out] response HTTP response
  */
-void ast_ari_bridges_create_or_update_with_id(struct ast_variable *headers, struct ast_ari_bridges_create_or_update_with_id_args *args, struct ast_ari_response *response);
+void ast_ari_bridges_create_with_id(struct ast_variable *headers, struct ast_ari_bridges_create_with_id_args *args, struct ast_ari_response *response);
 /*! Argument struct for ast_ari_bridges_get() */
 struct ast_ari_bridges_get_args {
 	/*! Bridge's id */
@@ -306,7 +306,7 @@ int ast_ari_bridges_play_with_id_parse_body(
 /*!
  * \brief Start playback of media on a bridge.
  *
- * The media URI may be any of a number of URI's. Currently sound: and recording: URI's are supported. This operation creates a playback resource that can be used to control the playback of media (pause, rewind, fast forward, etc.)
+ * The media URI may be any of a number of URI's. Currently sound:, recording:, number:, digits:, characters:, and tone: URI's are supported. This operation creates a playback resource that can be used to control the playback of media (pause, rewind, fast forward, etc.)
  *
  * \param headers HTTP headers
  * \param args Swagger parameters
diff --git a/res/ari/resource_channels.c b/res/ari/resource_channels.c
index 4159e93..f722802 100644
--- a/res/ari/resource_channels.c
+++ b/res/ari/resource_channels.c
@@ -24,13 +24,16 @@
  */
 
 /*** MODULEINFO
-	<depend type="module">res_stasis_app_playback</depend>
+	<depend type="module">res_stasis_answer</depend>
+	<depend type="module">res_stasis_playback</depend>
+	<depend type="module">res_stasis_recording</depend>
+	<depend type="module">res_stasis_snoop</depend>
 	<support_level>core</support_level>
  ***/
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 421312 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/file.h"
 #include "asterisk/pbx.h"
@@ -44,6 +47,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 421312 $")
 #include "asterisk/causes.h"
 #include "asterisk/format_cache.h"
 #include "asterisk/core_local.h"
+#include "asterisk/dial.h"
 #include "resource_channels.h"
 
 #include <limits.h>
@@ -90,6 +94,10 @@ void ast_ari_channels_continue_in_dialplan(
 	struct ast_ari_response *response)
 {
 	RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
+	int ipri;
+	const char *context;
+	const char *exten;
 
 	ast_assert(response != NULL);
 
@@ -98,7 +106,52 @@ void ast_ari_channels_continue_in_dialplan(
 		return;
 	}
 
-	if (stasis_app_control_continue(control, args->context, args->extension, args->priority)) {
+	snapshot = stasis_app_control_get_snapshot(control);
+	if (!snapshot) {
+		return;
+	}
+
+	if (ast_strlen_zero(args->context)) {
+		context = snapshot->context;
+		exten = S_OR(args->extension, snapshot->exten);
+	} else {
+		context = args->context;
+		exten = S_OR(args->extension, "s");
+	}
+
+	if (!ast_strlen_zero(args->label)) {
+		/* A label was provided in the request, use that */
+
+		if (sscanf(args->label, "%30d", &ipri) != 1) {
+			ipri = ast_findlabel_extension(NULL, context, exten, args->label, NULL);
+			if (ipri == -1) {
+				ast_log(AST_LOG_ERROR, "Requested label: %s can not be found in context: %s\n", args->label, context);
+				ast_ari_response_error(response, 404, "Not Found", "Requested label can not be found");
+				return;
+			}
+		} else {
+			ast_debug(3, "Numeric value provided for label, jumping to that priority\n");
+		}
+
+		if (ipri == 0) {
+			ast_log(AST_LOG_ERROR, "Invalid priority label '%s' specified for extension %s in context: %s\n",
+					args->label, exten, context);
+			ast_ari_response_error(response, 400, "Bad Request", "Requested priority is illegal");
+			return;
+		}
+
+	} else if (args->priority) {
+		/* No label provided, use provided priority */
+		ipri = args->priority;
+	} else if (ast_strlen_zero(args->context) && ast_strlen_zero(args->extension)) {
+		/* Special case. No exten, context, or priority provided, then move on to the next priority */
+		ipri = snapshot->priority + 1;
+	} else {
+		ipri = 1;
+	}
+
+
+	if (stasis_app_control_continue(control, context, exten, ipri)) {
 		ast_ari_response_alloc_failed(response);
 		return;
 	}
@@ -106,6 +159,64 @@ void ast_ari_channels_continue_in_dialplan(
 	ast_ari_response_no_content(response);
 }
 
+void ast_ari_channels_redirect(struct ast_variable *headers,
+	struct ast_ari_channels_redirect_args *args,
+	struct ast_ari_response *response)
+{
+	RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_channel_snapshot *, chan_snapshot, NULL, ao2_cleanup);
+	char *tech;
+	char *resource;
+	int tech_len;
+
+	control = find_control(response, args->channel_id);
+	if (!control) {
+		return;
+	}
+
+	if (ast_strlen_zero(args->endpoint)) {
+		ast_ari_response_error(response, 400, "Not Found",
+			"Required parameter 'endpoint' not provided.");
+		return;
+	}
+
+	tech = ast_strdupa(args->endpoint);
+	if (!(resource = strchr(tech, '/')) || !(tech_len = resource - tech)) {
+		ast_ari_response_error(response, 422, "Unprocessable Entity",
+			"Endpoint parameter '%s' does not contain tech/resource", args->endpoint);
+		return;
+	}
+
+	*resource++ = '\0';
+	if (ast_strlen_zero(resource)) {
+		ast_ari_response_error(response, 422, "Unprocessable Entity",
+			"No resource provided in endpoint parameter '%s'", args->endpoint);
+		return;
+	}
+
+	chan_snapshot = ast_channel_snapshot_get_latest(args->channel_id);
+	if (!chan_snapshot) {
+		ast_ari_response_error(response, 500, "Internal Server Error",
+			"Unable to find channel snapshot for '%s'", args->channel_id);
+		return;
+	}
+
+	if (strncasecmp(chan_snapshot->type, tech, tech_len)) {
+		ast_ari_response_error(response, 422, "Unprocessable Entity",
+			"Endpoint technology '%s' does not match channel technology '%s'",
+			tech, chan_snapshot->type);
+		return;
+	}
+
+	if (stasis_app_control_redirect(control, resource)) {
+		ast_ari_response_error(response, 500, "Internal Server Error",
+			"Failed to redirect channel");
+		return;
+	}
+
+	ast_ari_response_no_content(response);
+}
+
 void ast_ari_channels_answer(struct ast_variable *headers,
 	struct ast_ari_channels_answer_args *args,
 	struct ast_ari_response *response)
@@ -518,7 +629,7 @@ void ast_ari_channels_record(struct ast_variable *headers,
 		return;
 	}
 
-	if (options->if_exists == -1) {
+	if (options->if_exists == AST_RECORD_IF_EXISTS_ERROR) {
 		ast_ari_response_error(
 			response, 400, "Bad Request",
 			"ifExists invalid");
@@ -654,6 +765,8 @@ void ast_ari_channels_hangup(struct ast_variable *headers,
 		cause = AST_CAUSE_BUSY;
 	} else if (!strcmp(args->reason, "congestion")) {
 		cause = AST_CAUSE_CONGESTION;
+	} else if (!strcmp(args->reason, "no_answer")) {
+		cause = AST_CAUSE_NOANSWER;
 	} else {
 		ast_ari_response_error(
 			response, 400, "Invalid Reason",
@@ -723,10 +836,74 @@ void ast_ari_channels_list(struct ast_variable *headers,
 	ast_ari_response_ok(response, ast_json_ref(json));
 }
 
+/*! \brief Structure used for origination */
+struct ari_origination {
+	/*! \brief Dialplan context */
+	char context[AST_MAX_CONTEXT];
+	/*! \brief Dialplan extension */
+	char exten[AST_MAX_EXTENSION];
+	/*! \brief Dialplan priority */
+	int priority;
+	/*! \brief Application data to pass to Stasis application */
+	char appdata[0];
+};
+
+/*! \brief Thread which dials and executes upon answer */
+static void *ari_originate_dial(void *data)
+{
+	struct ast_dial *dial = data;
+	struct ari_origination *origination = ast_dial_get_user_data(dial);
+	enum ast_dial_result res;
+
+	res = ast_dial_run(dial, NULL, 0);
+	if (res != AST_DIAL_RESULT_ANSWERED) {
+		goto end;
+	}
+
+	if (!ast_strlen_zero(origination->appdata)) {
+		struct ast_app *app = pbx_findapp("Stasis");
+
+		if (app) {
+			ast_verb(4, "Launching Stasis(%s) on %s\n", origination->appdata,
+				ast_channel_name(ast_dial_answered(dial)));
+			pbx_exec(ast_dial_answered(dial), app, origination->appdata);
+		} else {
+			ast_log(LOG_WARNING, "No such application 'Stasis'\n");
+		}
+	} else {
+		struct ast_channel *answered = ast_dial_answered(dial);
+
+		if (!ast_strlen_zero(origination->context)) {
+			ast_channel_context_set(answered, origination->context);
+		}
+
+		if (!ast_strlen_zero(origination->exten)) {
+			ast_channel_exten_set(answered, origination->exten);
+		}
+
+		if (origination->priority > 0) {
+			ast_channel_priority_set(answered, origination->priority);
+		}
+
+		if (ast_pbx_run(answered)) {
+			ast_log(LOG_ERROR, "Failed to start PBX on %s\n", ast_channel_name(answered));
+		} else {
+			/* PBX will have taken care of hanging up, so we steal the answered channel so dial doesn't do it */
+			ast_dial_answered_steal(dial);
+		}
+	}
+
+end:
+	ast_dial_destroy(dial);
+	ast_free(origination);
+	return NULL;
+}
+
 static void ari_channels_handle_originate_with_id(const char *args_endpoint,
 	const char *args_extension,
 	const char *args_context,
 	long args_priority,
+	const char *args_label,
 	const char *args_app,
 	const char *args_app_args,
 	const char *args_caller_id,
@@ -734,29 +911,25 @@ static void ari_channels_handle_originate_with_id(const char *args_endpoint,
 	struct ast_variable *variables,
 	const char *args_channel_id,
 	const char *args_other_channel_id,
+	const char *args_originator,
 	struct ast_ari_response *response)
 {
 	char *dialtech;
 	char dialdevice[AST_CHANNEL_NAME];
+	struct ast_dial *dial;
 	char *caller_id = NULL;
 	char *cid_num = NULL;
 	char *cid_name = NULL;
-	int timeout = 30000;
-	RAII_VAR(struct ast_format_cap *, cap,
-		ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT), ao2_cleanup);
 	char *stuff;
-	struct ast_channel *chan;
+	struct ast_channel *other = NULL;
+	struct ast_channel *chan = NULL;
 	RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
 	struct ast_assigned_ids assignedids = {
 		.uniqueid = args_channel_id,
 		.uniqueid2 = args_other_channel_id,
 	};
-
-	if (!cap) {
-		ast_ari_response_alloc_failed(response);
-		return;
-	}
-	ast_format_cap_append(cap, ast_format_slin, 0);
+	struct ari_origination *origination;
+	pthread_t thread;
 
 	if ((assignedids.uniqueid && AST_MAX_PUBLIC_UNIQUEID < strlen(assignedids.uniqueid))
 		|| (assignedids.uniqueid2 && AST_MAX_PUBLIC_UNIQUEID < strlen(assignedids.uniqueid2))) {
@@ -783,24 +956,7 @@ static void ari_channels_handle_originate_with_id(const char *args_endpoint,
 		return;
 	}
 
-	if (args_timeout > 0) {
-		timeout = args_timeout * 1000;
-	} else if (args_timeout == -1) {
-		timeout = -1;
-	}
-
-	if (!ast_strlen_zero(args_caller_id)) {
-		caller_id = ast_strdupa(args_caller_id);
-		ast_callerid_parse(caller_id, &cid_name, &cid_num);
-
-		if (ast_is_shrinkable_phonenumber(cid_num)) {
-			ast_shrink_phone_number(cid_num);
-		}
-	}
-
 	if (!ast_strlen_zero(args_app)) {
-		const char *app = "Stasis";
-
 		RAII_VAR(struct ast_str *, appdata, ast_str_create(64), ast_free);
 
 		if (!appdata) {
@@ -813,23 +969,154 @@ static void ari_channels_handle_originate_with_id(const char *args_endpoint,
 			ast_str_append(&appdata, 0, ",%s", args_app_args);
 		}
 
-		/* originate a channel, putting it into an application */
-		if (ast_pbx_outgoing_app(dialtech, cap, dialdevice, timeout, app, ast_str_buffer(appdata), NULL, 0, cid_num, cid_name, variables, NULL, &chan, &assignedids)) {
+		origination = ast_calloc(1, sizeof(*origination) + ast_str_size(appdata) + 1);
+		if (!origination) {
 			ast_ari_response_alloc_failed(response);
 			return;
 		}
+
+		strcpy(origination->appdata, ast_str_buffer(appdata));
 	} else if (!ast_strlen_zero(args_extension)) {
-		/* originate a channel, sending it to an extension */
-		if (ast_pbx_outgoing_exten(dialtech, cap, dialdevice, timeout, S_OR(args_context, "default"), args_extension, args_priority ? args_priority : 1, NULL, 0, cid_num, cid_name, variables, NULL, &chan, 0, &assignedids)) {
+		origination = ast_calloc(1, sizeof(*origination) + 1);
+		if (!origination) {
 			ast_ari_response_alloc_failed(response);
 			return;
 		}
+
+		ast_copy_string(origination->context, S_OR(args_context, "default"), sizeof(origination->context));
+		ast_copy_string(origination->exten, args_extension, sizeof(origination->exten));
+
+		if (!ast_strlen_zero(args_label)) {
+			/* A label was provided in the request, use that */
+			int ipri = 1;
+			if (sscanf(args_label, "%30d", &ipri) != 1) {
+				ipri = ast_findlabel_extension(chan, origination->context, origination->exten, args_label, args_caller_id);
+
+				if (ipri == -1) {
+					ast_log(AST_LOG_ERROR, "Requested label: %s can not be found in context: %s\n", args_label, args_context);
+					ast_ari_response_error(response, 404, "Not Found", "Requested label can not be found");
+					return;
+				}
+			} else {
+				ast_debug(3, "Numeric value provided for label, jumping to that priority\n");
+			}
+
+			if (ipri == 0) {
+				ast_log(AST_LOG_ERROR, "Invalid priority label '%s' specified for extension %s in context: %s\n",
+						args_label, args_extension, args_context);
+				ast_ari_response_error(response, 400, "Bad Request", "Requested priority is illegal");
+				return;
+			}
+
+			/* Our priority was provided by a label */
+			origination->priority =  ipri;
+		} else {
+			/* No label provided, use provided priority */
+			origination->priority = args_priority ? args_priority : 1;
+		}
+
+		origination->appdata[0] = '\0';
 	} else {
 		ast_ari_response_error(response, 400, "Bad Request",
 			"Application or extension must be specified");
 		return;
 	}
 
+	dial = ast_dial_create();
+	if (!dial) {
+		ast_ari_response_alloc_failed(response);
+		ast_free(origination);
+		return;
+	}
+	ast_dial_set_user_data(dial, origination);
+
+	if (ast_dial_append(dial, dialtech, dialdevice, &assignedids)) {
+		ast_ari_response_alloc_failed(response);
+		ast_dial_destroy(dial);
+		ast_free(origination);
+		return;
+	}
+
+	if (args_timeout > 0) {
+		ast_dial_set_global_timeout(dial, args_timeout * 1000);
+	} else if (args_timeout == -1) {
+		ast_dial_set_global_timeout(dial, -1);
+	} else {
+		ast_dial_set_global_timeout(dial, 30000);
+	}
+
+	if (!ast_strlen_zero(args_caller_id)) {
+		caller_id = ast_strdupa(args_caller_id);
+		ast_callerid_parse(caller_id, &cid_name, &cid_num);
+
+		if (ast_is_shrinkable_phonenumber(cid_num)) {
+			ast_shrink_phone_number(cid_num);
+		}
+	}
+
+	if (!ast_strlen_zero(args_originator)) {
+		other = ast_channel_get_by_name(args_originator);
+		if (!other) {
+			ast_ari_response_error(
+				response, 400, "Bad Request",
+				"Provided originator channel was not found");
+			ast_dial_destroy(dial);
+			ast_free(origination);
+			return;
+		}
+	}
+
+	if (ast_dial_prerun(dial, other, NULL)) {
+		ast_ari_response_alloc_failed(response);
+		ast_dial_destroy(dial);
+		ast_free(origination);
+		ast_channel_cleanup(other);
+		return;
+	}
+
+	ast_channel_cleanup(other);
+
+	chan = ast_dial_get_channel(dial, 0);
+	if (!chan) {
+		ast_ari_response_alloc_failed(response);
+		ast_dial_destroy(dial);
+		ast_free(origination);
+		return;
+	}
+
+	if (!ast_strlen_zero(cid_num) || !ast_strlen_zero(cid_name)) {
+		struct ast_party_connected_line connected;
+
+		/*
+		 * It seems strange to set the CallerID on an outgoing call leg
+		 * to whom we are calling, but this function's callers are doing
+		 * various Originate methods.  This call leg goes to the local
+		 * user.  Once the called party answers, the dialplan needs to
+		 * be able to access the CallerID from the CALLERID function as
+		 * if the called party had placed this call.
+		 */
+		ast_set_callerid(chan, cid_num, cid_name, cid_num);
+
+		ast_party_connected_line_set_init(&connected, ast_channel_connected(chan));
+		if (!ast_strlen_zero(cid_num)) {
+			connected.id.number.valid = 1;
+			connected.id.number.str = (char *) cid_num;
+			connected.id.number.presentation = AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED;
+		}
+		if (!ast_strlen_zero(cid_name)) {
+			connected.id.name.valid = 1;
+			connected.id.name.str = (char *) cid_name;
+			connected.id.name.presentation = AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED;
+		}
+		ast_channel_set_connected_line(chan, &connected, NULL);
+	}
+
+	ast_channel_lock(chan);
+	if (variables) {
+		ast_set_variables(chan, variables);
+	}
+	ast_set_flag(ast_channel_flags(chan), AST_FLAG_ORIGINATED);
+
 	if (!ast_strlen_zero(args_app)) {
 		struct ast_channel *local_peer;
 
@@ -846,15 +1133,61 @@ static void ari_channels_handle_originate_with_id(const char *args_endpoint,
 	snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(chan));
 	ast_channel_unlock(chan);
 
-	ast_ari_response_ok(response, ast_channel_snapshot_to_json(snapshot, NULL));
+	/* Before starting the async dial bump the ref in case the dial quickly goes away and takes
+	 * the reference with it
+	 */
+	ast_channel_ref(chan);
+
+	if (ast_pthread_create_detached(&thread, NULL, ari_originate_dial, dial)) {
+		ast_ari_response_alloc_failed(response);
+		ast_dial_destroy(dial);
+		ast_free(origination);
+	} else {
+		ast_ari_response_ok(response, ast_channel_snapshot_to_json(snapshot, NULL));
+	}
+
 	ast_channel_unref(chan);
+	return;
+}
+
+/*!
+ * \internal
+ * \brief Convert a \c ast_json list of key/value pair tuples into a \c ast_variable list
+ * \since 13.3.0
+ *
+ * \param[out] response HTTP response if error
+ * \param json_variables The JSON blob containing the variable
+ * \param[out] variables An out reference to the variables to populate.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int json_to_ast_variables(struct ast_ari_response *response, struct ast_json *json_variables, struct ast_variable **variables)
+{
+	enum ast_json_to_ast_vars_code res;
+
+	res = ast_json_to_ast_variables(json_variables, variables);
+	switch (res) {
+	case AST_JSON_TO_AST_VARS_CODE_SUCCESS:
+		return 0;
+	case AST_JSON_TO_AST_VARS_CODE_INVALID_TYPE:
+		ast_ari_response_error(response, 400, "Bad Request",
+			"Only string values in the 'variables' object allowed");
+		break;
+	case AST_JSON_TO_AST_VARS_CODE_OOM:
+		ast_ari_response_alloc_failed(response);
+		break;
+	}
+	ast_log(AST_LOG_ERROR, "Unable to convert 'variables' in JSON body to channel variables\n");
+
+	return -1;
 }
 
 void ast_ari_channels_originate_with_id(struct ast_variable *headers,
 	struct ast_ari_channels_originate_with_id_args *args,
 	struct ast_ari_response *response)
 {
-	RAII_VAR(struct ast_variable *, variables, NULL, ast_variables_destroy);
+	struct ast_variable *variables = NULL;
 
 	/* Parse any query parameters out of the body parameter */
 	if (args->variables) {
@@ -862,12 +1195,9 @@ void ast_ari_channels_originate_with_id(struct ast_variable *headers,
 
 		ast_ari_channels_originate_with_id_parse_body(args->variables, args);
 		json_variables = ast_json_object_get(args->variables, "variables");
-		if (json_variables) {
-			if (ast_json_to_ast_variables(json_variables, &variables)) {
-				ast_log(AST_LOG_ERROR, "Unable to convert 'variables' in JSON body to channel variables\n");
-				ast_ari_response_alloc_failed(response);
-				return;
-			}
+		if (json_variables
+			&& json_to_ast_variables(response, json_variables, &variables)) {
+			return;
 		}
 	}
 
@@ -876,6 +1206,7 @@ void ast_ari_channels_originate_with_id(struct ast_variable *headers,
 		args->extension,
 		args->context,
 		args->priority,
+		args->label,
 		args->app,
 		args->app_args,
 		args->caller_id,
@@ -883,14 +1214,16 @@ void ast_ari_channels_originate_with_id(struct ast_variable *headers,
 		variables,
 		args->channel_id,
 		args->other_channel_id,
+		args->originator,
 		response);
+	ast_variables_destroy(variables);
 }
 
 void ast_ari_channels_originate(struct ast_variable *headers,
 	struct ast_ari_channels_originate_args *args,
 	struct ast_ari_response *response)
 {
-	RAII_VAR(struct ast_variable *, variables, NULL, ast_variables_destroy);
+	struct ast_variable *variables = NULL;
 
 	/* Parse any query parameters out of the body parameter */
 	if (args->variables) {
@@ -898,12 +1231,9 @@ void ast_ari_channels_originate(struct ast_variable *headers,
 
 		ast_ari_channels_originate_parse_body(args->variables, args);
 		json_variables = ast_json_object_get(args->variables, "variables");
-		if (json_variables) {
-			if (ast_json_to_ast_variables(json_variables, &variables)) {
-				ast_log(AST_LOG_ERROR, "Unable to convert 'variables' in JSON body to channel variables\n");
-				ast_ari_response_alloc_failed(response);
-				return;
-			}
+		if (json_variables
+			&& json_to_ast_variables(response, json_variables, &variables)) {
+			return;
 		}
 	}
 
@@ -912,6 +1242,7 @@ void ast_ari_channels_originate(struct ast_variable *headers,
 		args->extension,
 		args->context,
 		args->priority,
+		args->label,
 		args->app,
 		args->app_args,
 		args->caller_id,
@@ -919,7 +1250,9 @@ void ast_ari_channels_originate(struct ast_variable *headers,
 		variables,
 		args->channel_id,
 		args->other_channel_id,
+		args->originator,
 		response);
+	ast_variables_destroy(variables);
 }
 
 void ast_ari_channels_get_channel_var(struct ast_variable *headers,
@@ -933,6 +1266,11 @@ void ast_ari_channels_get_channel_var(struct ast_variable *headers,
 
 	ast_assert(response != NULL);
 
+	if (!value) {
+		ast_ari_response_alloc_failed(response);
+		return;
+	}
+
 	if (ast_strlen_zero(args->variable)) {
 		ast_ari_response_error(
 			response, 400, "Bad Request",
@@ -971,7 +1309,9 @@ void ast_ari_channels_get_channel_var(struct ast_variable *headers,
 		}
 	} else {
 		if (!ast_str_retrieve_variable(&value, 0, channel, NULL, args->variable)) {
-			ast_ari_response_alloc_failed(response);
+			ast_ari_response_error(
+				response, 404, "Variable Not Found",
+				"Provided variable was not found");
 			return;
 		}
 	}
diff --git a/res/ari/resource_channels.h b/res/ari/resource_channels.h
index 104e1bd..4d3ad5f 100644
--- a/res/ari/resource_channels.h
+++ b/res/ari/resource_channels.h
@@ -54,15 +54,17 @@ void ast_ari_channels_list(struct ast_variable *headers, struct ast_ari_channels
 struct ast_ari_channels_originate_args {
 	/*! Endpoint to call. */
 	const char *endpoint;
-	/*! The extension to dial after the endpoint answers */
+	/*! The extension to dial after the endpoint answers. Mutually exclusive with 'app'. */
 	const char *extension;
-	/*! The context to dial after the endpoint answers. If omitted, uses 'default' */
+	/*! The context to dial after the endpoint answers. If omitted, uses 'default'. Mutually exclusive with 'app'. */
 	const char *context;
-	/*! The priority to dial after the endpoint answers. If omitted, uses 1 */
+	/*! The priority to dial after the endpoint answers. If omitted, uses 1. Mutually exclusive with 'app'. */
 	long priority;
-	/*! The application that is subscribed to the originated channel, and passed to the Stasis application. */
+	/*! The label to dial after the endpoint answers. Will supersede 'priority' if provided. Mutually exclusive with 'app'. */
+	const char *label;
+	/*! The application that is subscribed to the originated channel. When the channel is answered, it will be passed to this Stasis application. Mutually exclusive with 'context', 'extension', 'priority', and 'label'. */
 	const char *app;
-	/*! The application arguments to pass to the Stasis application. */
+	/*! The application arguments to pass to the Stasis application provided by 'app'. Mutually exclusive with 'context', 'extension', 'priority', and 'label'. */
 	const char *app_args;
 	/*! CallerID to use when dialing the endpoint or extension. */
 	const char *caller_id;
@@ -74,6 +76,8 @@ struct ast_ari_channels_originate_args {
 	const char *channel_id;
 	/*! The unique id to assign the second channel when using local channels. */
 	const char *other_channel_id;
+	/*! The unique id of the channel which is originating this one. */
+	const char *originator;
 };
 /*!
  * \brief Body parsing function for /channels.
@@ -115,15 +119,17 @@ struct ast_ari_channels_originate_with_id_args {
 	const char *channel_id;
 	/*! Endpoint to call. */
 	const char *endpoint;
-	/*! The extension to dial after the endpoint answers */
+	/*! The extension to dial after the endpoint answers. Mutually exclusive with 'app'. */
 	const char *extension;
-	/*! The context to dial after the endpoint answers. If omitted, uses 'default' */
+	/*! The context to dial after the endpoint answers. If omitted, uses 'default'. Mutually exclusive with 'app'. */
 	const char *context;
-	/*! The priority to dial after the endpoint answers. If omitted, uses 1 */
+	/*! The priority to dial after the endpoint answers. If omitted, uses 1. Mutually exclusive with 'app'. */
 	long priority;
-	/*! The application that is subscribed to the originated channel, and passed to the Stasis application. */
+	/*! The label to dial after the endpoint answers. Will supersede 'priority' if provided. Mutually exclusive with 'app'. */
+	const char *label;
+	/*! The application that is subscribed to the originated channel. When the channel is answered, it will be passed to this Stasis application. Mutually exclusive with 'context', 'extension', 'priority', and 'label'. */
 	const char *app;
-	/*! The application arguments to pass to the Stasis application. */
+	/*! The application arguments to pass to the Stasis application provided by 'app'. Mutually exclusive with 'context', 'extension', 'priority', and 'label'. */
 	const char *app_args;
 	/*! CallerID to use when dialing the endpoint or extension. */
 	const char *caller_id;
@@ -133,6 +139,8 @@ struct ast_ari_channels_originate_with_id_args {
 	struct ast_json *variables;
 	/*! The unique id to assign the second channel when using local channels. */
 	const char *other_channel_id;
+	/*! The unique id of the channel which is originating this one. */
+	const char *originator;
 };
 /*!
  * \brief Body parsing function for /channels/{channelId}.
@@ -191,6 +199,8 @@ struct ast_ari_channels_continue_in_dialplan_args {
 	const char *extension;
 	/*! The priority to continue to. */
 	int priority;
+	/*! The label to continue to - will supersede 'priority' if both are provided. */
+	const char *label;
 };
 /*!
  * \brief Body parsing function for /channels/{channelId}/continue.
@@ -211,6 +221,32 @@ int ast_ari_channels_continue_in_dialplan_parse_body(
  * \param[out] response HTTP response
  */
 void ast_ari_channels_continue_in_dialplan(struct ast_variable *headers, struct ast_ari_channels_continue_in_dialplan_args *args, struct ast_ari_response *response);
+/*! Argument struct for ast_ari_channels_redirect() */
+struct ast_ari_channels_redirect_args {
+	/*! Channel's id */
+	const char *channel_id;
+	/*! The endpoint to redirect the channel to */
+	const char *endpoint;
+};
+/*!
+ * \brief Body parsing function for /channels/{channelId}/redirect.
+ * \param body The JSON body from which to parse parameters.
+ * \param[out] args The args structure to parse into.
+ * \retval zero on success
+ * \retval non-zero on failure
+ */
+int ast_ari_channels_redirect_parse_body(
+	struct ast_json *body,
+	struct ast_ari_channels_redirect_args *args);
+
+/*!
+ * \brief Redirect the channel to a different location.
+ *
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ * \param[out] response HTTP response
+ */
+void ast_ari_channels_redirect(struct ast_variable *headers, struct ast_ari_channels_redirect_args *args, struct ast_ari_response *response);
 /*! Argument struct for ast_ari_channels_answer() */
 struct ast_ari_channels_answer_args {
 	/*! Channel's id */
@@ -496,7 +532,7 @@ int ast_ari_channels_play_with_id_parse_body(
 /*!
  * \brief Start playback of media and specify the playbackId.
  *
- * The media URI may be any of a number of URI's. Currently sound: and recording: URI's are supported. This operation creates a playback resource that can be used to control the playback of media (pause, rewind, fast forward, etc.)
+ * The media URI may be any of a number of URI's. Currently sound:, recording:, number:, digits:, characters:, and tone: URI's are supported. This operation creates a playback resource that can be used to control the playback of media (pause, rewind, fast forward, etc.)
  *
  * \param headers HTTP headers
  * \param args Swagger parameters
diff --git a/res/ari/resource_device_states.c b/res/ari/resource_device_states.c
index 959f58e..9dec1b6 100644
--- a/res/ari/resource_device_states.c
+++ b/res/ari/resource_device_states.c
@@ -23,9 +23,14 @@
  * \author Kevin Harwell <kharwell at digium.com>
  */
 
+/*** MODULEINFO
+	<depend type="module">res_stasis_device_state</depend>
+	<support_level>core</support_level>
+ ***/
+
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 405326 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "resource_device_states.h"
 #include "asterisk/stasis_app_device_state.h"
diff --git a/res/ari/resource_endpoints.c b/res/ari/resource_endpoints.c
index 8d739f4..f794969 100644
--- a/res/ari/resource_endpoints.c
+++ b/res/ari/resource_endpoints.c
@@ -25,7 +25,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420098 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "resource_endpoints.h"
 
@@ -220,35 +220,66 @@ static void send_message(const char *to, const char *from, const char *body, str
 	response->response_text = "Accepted";
 }
 
+/*!
+ * \internal
+ * \brief Convert a \c ast_json list of key/value pair tuples into a \c ast_variable list
+ * \since 13.3.0
+ *
+ * \param[out] response HTTP response if error
+ * \param json_variables The JSON blob containing the variable
+ * \param[out] variables An out reference to the variables to populate.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int json_to_ast_variables(struct ast_ari_response *response, struct ast_json *json_variables, struct ast_variable **variables)
+{
+	enum ast_json_to_ast_vars_code res;
+
+	res = ast_json_to_ast_variables(json_variables, variables);
+	switch (res) {
+	case AST_JSON_TO_AST_VARS_CODE_SUCCESS:
+		return 0;
+	case AST_JSON_TO_AST_VARS_CODE_INVALID_TYPE:
+		ast_ari_response_error(response, 400, "Bad Request",
+			"Only string values in the 'variables' object allowed");
+		break;
+	case AST_JSON_TO_AST_VARS_CODE_OOM:
+		ast_ari_response_alloc_failed(response);
+		break;
+	}
+	ast_log(AST_LOG_ERROR, "Unable to convert 'variables' in JSON body to Asterisk variables\n");
+
+	return -1;
+}
+
 void ast_ari_endpoints_send_message(struct ast_variable *headers,
 	struct ast_ari_endpoints_send_message_args *args,
 	struct ast_ari_response *response)
 {
-	RAII_VAR(struct ast_variable *, variables, NULL, ast_variables_destroy);
+	struct ast_variable *variables = NULL;
 
 	if (args->variables) {
 		struct ast_json *json_variables;
 
 		ast_ari_endpoints_send_message_parse_body(args->variables, args);
 		json_variables = ast_json_object_get(args->variables, "variables");
-		if (json_variables) {
-			if (ast_json_to_ast_variables(json_variables, &variables)) {
-				ast_log(AST_LOG_ERROR, "Unable to convert 'variables' in JSON body to Asterisk variables\n");
-				ast_ari_response_alloc_failed(response);
-				return;
-			}
+		if (json_variables
+			&& json_to_ast_variables(response, json_variables, &variables)) {
+			return;
 		}
 	}
 
 	send_message(args->to, args->from, args->body, variables, response);
+	ast_variables_destroy(variables);
 }
 
 void ast_ari_endpoints_send_message_to_endpoint(struct ast_variable *headers,
 	struct ast_ari_endpoints_send_message_to_endpoint_args *args,
 	struct ast_ari_response *response)
 {
-	RAII_VAR(struct ast_variable *, variables, NULL, ast_variables_destroy);
-	RAII_VAR(struct ast_endpoint_snapshot *, snapshot, NULL, ao2_cleanup);
+	struct ast_variable *variables = NULL;
+	struct ast_endpoint_snapshot *snapshot;
 	char msg_to[128];
 	char *tech = ast_strdupa(args->tech);
 
@@ -259,23 +290,21 @@ void ast_ari_endpoints_send_message_to_endpoint(struct ast_variable *headers,
 			"Endpoint not found");
 		return;
 	}
+	ao2_ref(snapshot, -1);
 
 	if (args->variables) {
 		struct ast_json *json_variables;
 
 		ast_ari_endpoints_send_message_to_endpoint_parse_body(args->variables, args);
 		json_variables = ast_json_object_get(args->variables, "variables");
-
-		if (json_variables) {
-			if (ast_json_to_ast_variables(json_variables, &variables)) {
-				ast_log(AST_LOG_ERROR, "Unable to convert 'variables' in JSON body to Asterisk variables\n");
-				ast_ari_response_alloc_failed(response);
-				return;
-			}
+		if (json_variables
+			&& json_to_ast_variables(response, json_variables, &variables)) {
+			return;
 		}
 	}
 
 	snprintf(msg_to, sizeof(msg_to), "%s:%s", ast_str_to_lower(tech), args->resource);
 
 	send_message(msg_to, args->from, args->body, variables, response);
+	ast_variables_destroy(variables);
 }
diff --git a/res/ari/resource_events.c b/res/ari/resource_events.c
index 1a43816..71d54b4 100644
--- a/res/ari/resource_events.c
+++ b/res/ari/resource_events.c
@@ -25,7 +25,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 414406 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/astobj2.h"
 #include "asterisk/stasis_app.h"
@@ -119,6 +119,10 @@ static void app_handler(void *data, const char *app_name,
 	const char *msg_application = S_OR(
 		ast_json_string_get(ast_json_object_get(message, "application")),
 		"");
+
+	if (!session) {
+		return;
+	}
  
 	/* Determine if we've been replaced */
 	if (strcmp(msg_type, "ApplicationReplaced") == 0 &&
@@ -144,9 +148,11 @@ static void app_handler(void *data, const char *app_name,
  * \brief Register for all of the apps given.
  * \param session Session info struct.
  * \param app_name Name of application to register.
+ * \param register_handler Pointer to the application registration handler
  */
 static int session_register_app(struct event_session *session,
-				 const char *app_name)
+				 const char *app_name,
+				 int (* register_handler)(const char *, stasis_app_cb handler, void *data))
 {
 	SCOPED_AO2LOCK(lock, session);
 
@@ -163,12 +169,52 @@ static int session_register_app(struct event_session *session,
 		return -1;
 	}
 
-	stasis_app_register(app_name, app_handler, session);
+	register_handler(app_name, app_handler, session);
 
 	return 0;
 }
 
-void ast_ari_websocket_events_event_websocket(struct ast_ari_websocket_session *ws_session,
+int ast_ari_websocket_events_event_websocket_attempted(struct ast_tcptls_session_instance *ser,
+	struct ast_variable *headers,
+	struct ast_ari_events_event_websocket_args *args)
+{
+	int res = 0;
+	size_t i, j;
+	int (* register_handler)(const char *, stasis_app_cb handler, void *data);
+
+	ast_debug(3, "/events WebSocket attempted\n");
+
+	if (args->app_count == 0) {
+		ast_http_error(ser, 400, "Bad Request", "Missing param 'app'");
+		return -1;
+	}
+
+	if (args->subscribe_all) {
+		register_handler = &stasis_app_register_all;
+	} else {
+		register_handler = &stasis_app_register;
+	}
+
+	for (i = 0; i < args->app_count; ++i) {
+		if (ast_strlen_zero(args->app[i])) {
+			res = -1;
+			break;
+		}
+
+		res |= register_handler(args->app[i], app_handler, NULL);
+	}
+
+	if (res) {
+		for (j = 0; j < i; ++j) {
+			stasis_app_unregister(args->app[j]);
+		}
+		ast_http_error(ser, 400, "Bad Request", "Invalid application provided in param 'app'.");
+	}
+
+	return res;
+}
+
+void ast_ari_websocket_events_event_websocket_established(struct ast_ari_websocket_session *ws_session,
 	struct ast_variable *headers,
 	struct ast_ari_events_event_websocket_args *args)
 {
@@ -176,6 +222,7 @@ void ast_ari_websocket_events_event_websocket(struct ast_ari_websocket_session *
 	struct ast_json *msg;
 	int res;
 	size_t i;
+	int (* register_handler)(const char *, stasis_app_cb handler, void *data);
 
 	ast_debug(3, "/events WebSocket connection\n");
 
@@ -185,12 +232,18 @@ void ast_ari_websocket_events_event_websocket(struct ast_ari_websocket_session *
 		return;
 	}
 
+	if (args->subscribe_all) {
+		register_handler = &stasis_app_register_all;
+	} else {
+		register_handler = &stasis_app_register;
+	}
+
 	res = 0;
 	for (i = 0; i < args->app_count; ++i) {
 		if (ast_strlen_zero(args->app[i])) {
 			continue;
 		}
-		res |= session_register_app(session, args->app[i]);
+		res |= session_register_app(session, args->app[i], register_handler);
 	}
 
 	if (ao2_container_count(session->websocket_apps) == 0) {
diff --git a/res/ari/resource_events.h b/res/ari/resource_events.h
index 646cf9b..c482699 100644
--- a/res/ari/resource_events.h
+++ b/res/ari/resource_events.h
@@ -47,7 +47,22 @@ struct ast_ari_events_event_websocket_args {
 	size_t app_count;
 	/*! Parsing context for app. */
 	char *app_parse;
+	/*! Subscribe to all Asterisk events. If provided, the applications listed will be subscribed to all events, effectively disabling the application specific subscriptions. Default is 'false'. */
+	int subscribe_all;
 };
+
+/*!
+ * \brief WebSocket connection for events.
+ *
+ * \param ser HTTP TCP/TLS Server Session
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ *
+ * \retval 0 success
+ * \retval non-zero error
+ */
+int ast_ari_websocket_events_event_websocket_attempted(struct ast_tcptls_session_instance *ser, struct ast_variable *headers, struct ast_ari_events_event_websocket_args *args);
+
 /*!
  * \brief WebSocket connection for events.
  *
@@ -55,7 +70,7 @@ struct ast_ari_events_event_websocket_args {
  * \param headers HTTP headers.
  * \param args Swagger parameters.
  */
-void ast_ari_websocket_events_event_websocket(struct ast_ari_websocket_session *session, struct ast_variable *headers, struct ast_ari_events_event_websocket_args *args);
+void ast_ari_websocket_events_event_websocket_established(struct ast_ari_websocket_session *session, struct ast_variable *headers, struct ast_ari_events_event_websocket_args *args);
 /*! Argument struct for ast_ari_events_user_event() */
 struct ast_ari_events_user_event_args {
 	/*! Event name */
diff --git a/res/ari/resource_mailboxes.c b/res/ari/resource_mailboxes.c
index ddeea0a..ec4c8e8 100644
--- a/res/ari/resource_mailboxes.c
+++ b/res/ari/resource_mailboxes.c
@@ -23,10 +23,15 @@
  * \author Jonathan Rose <jrose at digium.com>
  */
 
+/*** MODULEINFO
+	<depend type="module">res_stasis_mailbox</depend>
+	<support_level>core</support_level>
+ ***/
+
 #include "asterisk.h"
 #include "asterisk/stasis_app_mailbox.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 405554 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "resource_mailboxes.h"
 
diff --git a/res/ari/resource_playbacks.c b/res/ari/resource_playbacks.c
index 04abcc3..9f59754 100644
--- a/res/ari/resource_playbacks.c
+++ b/res/ari/resource_playbacks.c
@@ -23,9 +23,14 @@
  * \author David M. Lee, II <dlee at digium.com>
  */
 
+/*** MODULEINFO
+	<depend type="module">res_stasis_playback</depend>
+	<support_level>core</support_level>
+ ***/
+
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 405326 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/stasis_app_playback.h"
 #include "resource_playbacks.h"
diff --git a/res/ari/resource_recordings.c b/res/ari/resource_recordings.c
index efd6c43..28fa735 100644
--- a/res/ari/resource_recordings.c
+++ b/res/ari/resource_recordings.c
@@ -23,9 +23,14 @@
  * \author David M. Lee, II <dlee at digium.com>
  */
 
+/*** MODULEINFO
+	<depend type="module">res_stasis_recording</depend>
+	<support_level>core</support_level>
+ ***/
+
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419022 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/stasis_app_recording.h"
 #include "resource_recordings.h"
diff --git a/res/ari/resource_sounds.c b/res/ari/resource_sounds.c
index 36bc06c..5a523d3 100644
--- a/res/ari/resource_sounds.c
+++ b/res/ari/resource_sounds.c
@@ -25,7 +25,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419044 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "resource_sounds.h"
 #include "asterisk/media_index.h"
diff --git a/res/parking/parking_applications.c b/res/parking/parking_applications.c
index b4a56b2..d8cda6f 100644
--- a/res/parking/parking_applications.c
+++ b/res/parking/parking_applications.c
@@ -25,7 +25,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428687 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "res_parking.h"
 #include "asterisk/config.h"
@@ -349,15 +349,21 @@ static int setup_park_common_datastore(struct ast_channel *parkee, const char *p
 	attended_transfer = pbx_builtin_getvar_helper(parkee, "ATTENDEDTRANSFER");
 	blind_transfer = pbx_builtin_getvar_helper(parkee, "BLINDTRANSFER");
 
-	if (attended_transfer || blind_transfer) {
-		parker_dial_string = ast_strdupa(S_OR(attended_transfer, blind_transfer));
+	if (!ast_strlen_zero(attended_transfer)) {
+		parker_dial_string = ast_strdupa(attended_transfer);
+	} else if (!ast_strlen_zero(blind_transfer)) {
+		parker_dial_string = ast_strdupa(blind_transfer);
+		/* Ensure that attended_transfer is NULL and not an empty string. */
+		attended_transfer = NULL;
 	}
 
 	ast_channel_unlock(parkee);
 
 	if (!ast_strlen_zero(parker_dial_string)) {
 		ast_channel_name_to_dial_string(parker_dial_string);
-		ast_verb(4, "Setting Parker dial string to %s from %s value", parker_dial_string, attended_transfer ? "ATTENDEDTRANSFER" : "BLINDTRANSFER");
+		ast_verb(4, "Setting Parker dial string to %s from %s value\n",
+			parker_dial_string,
+			attended_transfer ? "ATTENDEDTRANSFER" : "BLINDTRANSFER");
 		park_datastore->parker_dial_string = ast_strdup(parker_dial_string);
 	}
 
@@ -610,6 +616,7 @@ static int parked_call_app_exec(struct ast_channel *chan, const char *data)
 	}
 
 	/* The parked call needs to know who is retrieving it before we move it out of the parking bridge */
+	ast_assert(pu->retriever == NULL);
 	pu->retriever = ast_channel_snapshot_create(chan);
 
 	/* Create bridge */
@@ -646,7 +653,10 @@ static int parked_call_app_exec(struct ast_channel *chan, const char *data)
 
 	ast_bridge_features_cleanup(&chan_features);
 
-	return 0;
+	/* Return -1 so that call does not continue in the dialplan. This is to make
+	 * behavior consistent with Asterisk versions prior to 12.
+	 */
+	return -1;
 }
 
 struct park_announce_subscription_data {
@@ -684,6 +694,68 @@ static struct park_announce_subscription_data *park_announce_subscription_data_c
 	return pa_data;
 }
 
+/*! \internal
+ * \brief Gathers inheritable channel variables from a channel by name.
+ *
+ * \param oh outgoing helper struct we are bestowing inheritable variables to
+ * \param channel_id name or uniqueID of the channel to inherit variables from
+ *
+ * \return Nothing
+ */
+static void inherit_channel_vars_from_id(struct outgoing_helper *oh, const char *channel_id)
+{
+	struct ast_channel *chan = ast_channel_get_by_name(channel_id);
+	struct ast_var_t *current;
+	struct ast_variable *newvar;
+	const char *varname;
+	int vartype;
+
+
+	if (!chan) {
+		/* Already gone */
+		return;
+	}
+
+	ast_channel_lock(chan);
+
+	AST_LIST_TRAVERSE(ast_channel_varshead((struct ast_channel *) chan), current, entries) {
+		varname = ast_var_full_name(current);
+		if (!varname) {
+			continue;
+		}
+
+		vartype = 0;
+		if (varname[0] == '_') {
+			vartype = 1;
+			if (varname[1] == '_') {
+				vartype = 2;
+			}
+		}
+
+		switch (vartype) {
+		case 1:
+			newvar = ast_variable_new(&varname[1], ast_var_value(current), "");
+			break;
+		case 2:
+			newvar = ast_variable_new(varname, ast_var_value(current), "");
+			break;
+		default:
+			continue;
+		}
+		if (newvar) {
+			ast_debug(1, "Inheriting variable %s from %s.\n",
+				newvar->name, ast_channel_name(chan));
+			if (oh->vars) {
+				newvar->next = oh->vars;
+				oh->vars = newvar;
+			}
+		}
+	}
+
+	ast_channel_unlock(chan);
+	ast_channel_cleanup(chan);
+}
+
 static void announce_to_dial(char *dial_string, char *announce_string, int parkingspace, struct ast_channel_snapshot *parkee_snapshot)
 {
 	struct ast_channel *dchan;
@@ -705,6 +777,9 @@ static void announce_to_dial(char *dial_string, char *announce_string, int parki
 
 	snprintf(buf, sizeof(buf), "%d", parkingspace);
 	oh.vars = ast_variable_new("_PARKEDAT", buf, "");
+
+	inherit_channel_vars_from_id(&oh, parkee_snapshot->uniqueid);
+
 	dchan = __ast_request_and_dial(dial_tech, cap_slin, NULL, NULL, dial_string, 30000,
 		&outstate,
 		parkee_snapshot->caller_number,
diff --git a/res/parking/parking_bridge_features.c b/res/parking/parking_bridge_features.c
index c6e01c9..a21be90 100644
--- a/res/parking/parking_bridge_features.c
+++ b/res/parking/parking_bridge_features.c
@@ -25,7 +25,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428687 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "res_parking.h"
 #include "asterisk/utils.h"
diff --git a/res/parking/parking_manager.c b/res/parking/parking_manager.c
index df0c4ce..435200b 100644
--- a/res/parking/parking_manager.c
+++ b/res/parking/parking_manager.c
@@ -25,7 +25,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420124 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "res_parking.h"
 #include "asterisk/config.h"
@@ -156,6 +156,22 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420124 $")
 			</syntax>
 		</managerEventInstance>
 	</managerEvent>
+	<managerEvent language="en_US" name="ParkedCallSwap">
+		<managerEventInstance class="EVENT_FLAG_CALL">
+			<synopsis>Raised when a channel takes the place of a previously parked channel</synopsis>
+			<syntax>
+				<channel_snapshot prefix="Parkee"/>
+				<channel_snapshot prefix="Parker"/>
+				<xi:include xpointer="xpointer(/docs/managerEvent[@name='ParkedCall']/managerEventInstance/syntax/parameter)" />
+			</syntax>
+			<description>
+				<para>This event is raised when a channel initially parked in the parking lot
+				is swapped out with a different channel. The most common case for this is when
+				an attended transfer to a parking lot occurs. The Parkee information in the event
+				will indicate the party that was swapped into the parking lot.</para>
+			</description>
+		</managerEventInstance>
+	</managerEvent>
  ***/
 
 /*! \brief subscription to the parking lot topic */
@@ -210,11 +226,16 @@ static struct ast_str *manager_build_parked_call_string(const struct ast_parked_
 
 	parkee_string = ast_manager_build_channel_state_string_prefix(payload->parkee, "Parkee");
 	if (!parkee_string) {
+		ast_free(out);
 		return NULL;
 	}
 
 	if (payload->retriever) {
 		retriever_string = ast_manager_build_channel_state_string_prefix(payload->retriever, "Retriever");
+		if (!retriever_string) {
+			ast_free(out);
+			return NULL;
+		}
 	}
 
 	ast_str_set(&out, 0,
@@ -250,7 +271,7 @@ static void manager_parking_status_single_lot(struct mansession *s, const struct
 		return;
 	}
 
-	astman_send_ack(s, m, "Parked calls will follow");
+	astman_send_listack(s, m, "Parked calls will follow", "start");
 
 	iter_users = ao2_iterator_init(curlot->parked_users, 0);
 	while ((curuser = ao2_iterator_next(&iter_users))) {
@@ -260,17 +281,13 @@ static void manager_parking_status_single_lot(struct mansession *s, const struct
 		payload = parked_call_payload_from_parked_user(curuser, PARKED_CALL);
 		if (!payload) {
 			ao2_ref(curuser, -1);
-			ao2_iterator_destroy(&iter_users);
-			astman_send_error(s, m, "Failed to retrieve parking data about a parked user.");
-			return;
+			break;
 		}
 
 		parked_call_string = manager_build_parked_call_string(payload);
 		if (!parked_call_string) {
 			ao2_ref(curuser, -1);
-			ao2_iterator_destroy(&iter_users);
-			astman_send_error(s, m, "Failed to retrieve parking data about a parked user.");
-			return;
+			break;
 		}
 
 		total++;
@@ -286,12 +303,9 @@ static void manager_parking_status_single_lot(struct mansession *s, const struct
 	}
 	ao2_iterator_destroy(&iter_users);
 
-	astman_append(s,
-		"Event: ParkedCallsComplete\r\n"
-		"Total: %d\r\n"
-		"%s"
-		"\r\n",
-		total, id_text);
+	astman_send_list_complete_start(s, m, "ParkedCallsComplete", total);
+	astman_append(s, "Total: %d\r\n", total);
+	astman_send_list_complete_end(s);
 }
 
 static void manager_parking_status_all_lots(struct mansession *s, const struct message *m, const char *id_text)
@@ -310,7 +324,7 @@ static void manager_parking_status_all_lots(struct mansession *s, const struct m
 		return;
 	}
 
-	astman_send_ack(s, m, "Parked calls will follow");
+	astman_send_listack(s, m, "Parked calls will follow", "start");
 
 	iter_lots = ao2_iterator_init(lot_container, 0);
 	while ((curlot = ao2_iterator_next(&iter_lots))) {
@@ -324,8 +338,7 @@ static void manager_parking_status_all_lots(struct mansession *s, const struct m
 				ao2_ref(curuser, -1);
 				ao2_iterator_destroy(&iter_users);
 				ao2_ref(curlot, -1);
-				ao2_iterator_destroy(&iter_lots);
-				return;
+				goto abort_list;
 			}
 
 			parked_call_string = manager_build_parked_call_string(payload);
@@ -333,8 +346,7 @@ static void manager_parking_status_all_lots(struct mansession *s, const struct m
 				ao2_ref(curuser, -1);
 				ao2_iterator_destroy(&iter_users);
 				ao2_ref(curlot, -1);
-				ao2_iterator_destroy(&iter_lots);
-				return;
+				goto abort_list;
 			}
 
 			total++;
@@ -351,22 +363,21 @@ static void manager_parking_status_all_lots(struct mansession *s, const struct m
 		ao2_iterator_destroy(&iter_users);
 		ao2_ref(curlot, -1);
 	}
+abort_list:
 	ao2_iterator_destroy(&iter_lots);
 
-	astman_append(s,
-		"Event: ParkedCallsComplete\r\n"
-		"Total: %d\r\n"
-		"%s"
-		"\r\n",
-		total, id_text);
+	astman_send_list_complete_start(s, m, "ParkedCallsComplete", total);
+	astman_append(s, "Total: %d\r\n", total);
+	astman_send_list_complete_end(s);
 }
 
 static int manager_parking_status(struct mansession *s, const struct message *m)
 {
 	const char *id = astman_get_header(m, "ActionID");
 	const char *lot_name = astman_get_header(m, "ParkingLot");
-	char id_text[256] = "";
+	char id_text[256];
 
+	id_text[0] = '\0';
 	if (!ast_strlen_zero(id)) {
 		snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
 	}
@@ -380,24 +391,30 @@ static int manager_parking_status(struct mansession *s, const struct message *m)
 	return 0;
 }
 
+struct park_list_data {
+	const char *id_text;
+	int count;
+};
+
 static int manager_append_event_parking_lot_data_cb(void *obj, void *arg, void *data, int flags)
 {
 	struct parking_lot *curlot = obj;
 	struct mansession *s = arg;
-	char *id_text = data;
+	struct park_list_data *list_data = data;
 
 	astman_append(s, "Event: Parkinglot\r\n"
+		"%s" /* The Action ID */
 		"Name: %s\r\n"
 		"StartSpace: %d\r\n"
 		"StopSpace: %d\r\n"
 		"Timeout: %u\r\n"
-		"%s" /* The Action ID */
 		"\r\n",
+		list_data->id_text,
 		curlot->name,
 		curlot->cfg->parking_start,
 		curlot->cfg->parking_stop,
-		curlot->cfg->parkingtime,
-		id_text);
+		curlot->cfg->parkingtime);
+	++list_data->count;
 
 	return 0;
 }
@@ -405,9 +422,11 @@ static int manager_append_event_parking_lot_data_cb(void *obj, void *arg, void *
 static int manager_parking_lot_list(struct mansession *s, const struct message *m)
 {
 	const char *id = astman_get_header(m, "ActionID");
-	char id_text[256] = "";
 	struct ao2_container *lot_container;
+	char id_text[256];
+	struct park_list_data list_data;
 
+	id_text[0] = '\0';
 	if (!ast_strlen_zero(id)) {
 		snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
 	}
@@ -419,14 +438,15 @@ static int manager_parking_lot_list(struct mansession *s, const struct message *
 		return 0;
 	}
 
-	astman_send_ack(s, m, "Parking lots will follow");
+	astman_send_listack(s, m, "Parking lots will follow", "start");
 
-	ao2_callback_data(lot_container, OBJ_MULTIPLE | OBJ_NODATA, manager_append_event_parking_lot_data_cb, s, id_text);
+	list_data.id_text = id_text;
+	list_data.count = 0;
+	ao2_callback_data(lot_container, OBJ_MULTIPLE | OBJ_NODATA,
+		manager_append_event_parking_lot_data_cb, s, &list_data);
 
-	astman_append(s,
-		"Event: ParkinglotsComplete\r\n"
-		"%s"
-		"\r\n",id_text);
+	astman_send_list_complete_start(s, m, "ParkinglotsComplete", list_data.count);
+	astman_send_list_complete_end(s);
 
 	return 0;
 }
@@ -685,7 +705,7 @@ int load_parking_manager(void)
 
 static void parking_manager_disable_stasis(void)
 {
-	parking_sub = stasis_unsubscribe(parking_sub);
+	parking_sub = stasis_unsubscribe_and_join(parking_sub);
 }
 
 void unload_parking_manager(void)
diff --git a/res/parking/parking_tests.c b/res/parking/parking_tests.c
index dd1d554..91cea26 100644
--- a/res/parking/parking_tests.c
+++ b/res/parking/parking_tests.c
@@ -25,7 +25,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428169 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "res_parking.h"
 #include "asterisk/utils.h"
@@ -137,15 +137,6 @@ static void do_sleep(struct timespec *to_sleep)
 	}
 }
 
-static int fake_fixup(struct ast_channel *clonechan, struct ast_channel *original)
-{
-	return 0;
-}
-
-static const struct ast_channel_tech fake_tech = {
-	.fixup = fake_fixup, /* silence warning from masquerade... though those shouldn't be happening now */
-};
-
 #define TEST_LOT_NAME "unit_tests_res_parking_test_lot"
 
 static struct parking_lot *generate_test_parking_lot(const char *name, int low_space, int high_space, const char *park_exten, const char *park_context, struct ast_test *test)
diff --git a/res/parking/parking_ui.c b/res/parking/parking_ui.c
index 46ef769..55b2d5b 100644
--- a/res/parking/parking_ui.c
+++ b/res/parking/parking_ui.c
@@ -25,7 +25,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 396887 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "res_parking.h"
 #include "asterisk/config.h"
diff --git a/res/res_adsi.c b/res/res_adsi.c
index 6287161..e4d3404 100644
--- a/res/res_adsi.c
+++ b/res/res_adsi.c
@@ -36,7 +36,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <time.h>
 #include <math.h>
diff --git a/res/res_ael_share.c b/res/res_ael_share.c
index 93c818a..bbe870b 100644
--- a/res/res_ael_share.c
+++ b/res/res_ael_share.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/file.h"
 #include "asterisk/channel.h"
diff --git a/res/res_agi.c b/res/res_agi.c
index b0ae0c3..ff33580 100644
--- a/res/res_agi.c
+++ b/res/res_agi.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 426362 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <math.h>
 #include <signal.h>
@@ -2210,8 +2210,9 @@ static int handle_streamfile(struct ast_channel *chan, AGI *agi, int argc, const
 	if ((vfs = ast_openvstream(chan, argv[2], ast_channel_language(chan)))) {
 		ast_debug(1, "Ooh, found a video stream, too\n");
 	}
-
-	ast_verb(3, "Playing '%s' (escape_digits=%s) (sample_offset %ld)\n", argv[2], edigits, sample_offset);
+	ast_verb(3, "<%s> Playing '%s.%s' (escape_digits=%s) (sample_offset %ld) (language '%s')\n",
+		ast_channel_name(chan), argv[2], ast_format_get_name(ast_channel_writeformat(chan)),
+		edigits, sample_offset, S_OR(ast_channel_language(chan), "default"));
 
 	ast_seekstream(fs, 0, SEEK_END);
 	max_length = ast_tellstream(fs);
@@ -2629,8 +2630,8 @@ static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, const
 			}
 			f = ast_read(chan);
 			if (!f) {
-				ast_agi_send(agi->fd, chan, "200 result=%d (hangup) endpos=%ld\n", -1, sample_offset);
 				ast_closestream(fs);
+				ast_agi_send(agi->fd, chan, "200 result=%d (hangup) endpos=%ld\n", -1, sample_offset);
 				if (sildet)
 					ast_dsp_free(sildet);
 				return RESULT_FAILURE;
@@ -2644,8 +2645,8 @@ static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, const
 					ast_stream_rewind(fs, 200);
 					ast_truncstream(fs);
 					sample_offset = ast_tellstream(fs);
-					ast_agi_send(agi->fd, chan, "200 result=%d (dtmf) endpos=%ld\n", f->subclass.integer, sample_offset);
 					ast_closestream(fs);
+					ast_agi_send(agi->fd, chan, "200 result=%d (dtmf) endpos=%ld\n", f->subclass.integer, sample_offset);
 					ast_frfree(f);
 					if (sildet)
 						ast_dsp_free(sildet);
@@ -2689,8 +2690,8 @@ static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, const
 			ast_truncstream(fs);
 			sample_offset = ast_tellstream(fs);
 		}
-		ast_agi_send(agi->fd, chan, "200 result=%d (timeout) endpos=%ld\n", res, sample_offset);
 		ast_closestream(fs);
+		ast_agi_send(agi->fd, chan, "200 result=%d (timeout) endpos=%ld\n", res, sample_offset);
 	}
 
 	if (silence > 0) {
diff --git a/res/res_ari.c b/res/res_ari.c
index b6c5284..f39db16 100644
--- a/res/res_ari.c
+++ b/res/res_ari.c
@@ -140,7 +140,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "ari/internal.h"
 #include "asterisk/ari.h"
@@ -180,8 +180,7 @@ int ast_ari_add_handler(struct stasis_rest_handlers *handler)
 
 	SCOPED_MUTEX(lock, &root_handler_lock);
 
-	old_size = sizeof(*new_handler) +
-		root_handler->num_children * sizeof(handler);
+	old_size = sizeof(*new_handler) + root_handler->num_children * sizeof(handler);
 	new_size = old_size + sizeof(handler);
 
 	new_handler = ao2_alloc(new_size, NULL);
@@ -200,21 +199,24 @@ int ast_ari_add_handler(struct stasis_rest_handlers *handler)
 
 int ast_ari_remove_handler(struct stasis_rest_handlers *handler)
 {
-	RAII_VAR(struct stasis_rest_handlers *, new_handler, NULL, ao2_cleanup);
-	size_t size, i, j;
+	struct stasis_rest_handlers *new_handler;
+	size_t size;
+	size_t i;
+	size_t j;
 
 	ast_assert(root_handler != NULL);
 
 	ast_mutex_lock(&root_handler_lock);
-	size = sizeof(*new_handler) +
-		root_handler->num_children * sizeof(handler);
+	size = sizeof(*new_handler) + root_handler->num_children * sizeof(handler);
 
 	new_handler = ao2_alloc(size, NULL);
 	if (!new_handler) {
+		ast_mutex_unlock(&root_handler_lock);
 		return -1;
 	}
-	memcpy(new_handler, root_handler, sizeof(*new_handler));
 
+	/* Create replacement root_handler less the handler to remove. */
+	memcpy(new_handler, root_handler, sizeof(*new_handler));
 	for (i = 0, j = 0; i < root_handler->num_children; ++i) {
 		if (root_handler->children[i] == handler) {
 			ast_module_unref(ast_module_info->self);
@@ -224,9 +226,10 @@ int ast_ari_remove_handler(struct stasis_rest_handlers *handler)
 	}
 	new_handler->num_children = j;
 
+	/* Replace the old root_handler with the new. */
 	ao2_cleanup(root_handler);
-	ao2_ref(new_handler, +1);
 	root_handler = new_handler;
+
 	ast_mutex_unlock(&root_handler_lock);
 	return 0;
 }
@@ -284,6 +287,13 @@ void ast_ari_response_no_content(struct ast_ari_response *response)
 	response->response_text = "No Content";
 }
 
+void ast_ari_response_accepted(struct ast_ari_response *response)
+{
+	response->message = ast_json_null();
+	response->response_code = 202;
+	response->response_text = "Accepted";
+}
+
 void ast_ari_response_alloc_failed(struct ast_ari_response *response)
 {
 	response->message = ast_json_ref(oom_json);
diff --git a/res/res_ari_applications.c b/res/res_ari_applications.c
index 882b618..e81d164 100644
--- a/res/res_ari_applications.c
+++ b/res/res_ari_applications.c
@@ -33,13 +33,14 @@
 
 /*** MODULEINFO
 	<depend type="module">res_ari</depend>
+	<depend type="module">res_ari_model</depend>
 	<depend type="module">res_stasis</depend>
 	<support_level>core</support_level>
  ***/
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/app.h"
 #include "asterisk/module.h"
diff --git a/res/res_ari_asterisk.c b/res/res_ari_asterisk.c
index 05f6567..fe6e3d3 100644
--- a/res/res_ari_asterisk.c
+++ b/res/res_ari_asterisk.c
@@ -33,13 +33,14 @@
 
 /*** MODULEINFO
 	<depend type="module">res_ari</depend>
+	<depend type="module">res_ari_model</depend>
 	<depend type="module">res_stasis</depend>
 	<support_level>core</support_level>
  ***/
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/app.h"
 #include "asterisk/module.h"
@@ -51,57 +52,752 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
 
 #define MAX_VALS 128
 
+/*!
+ * \brief Parameter parsing callback for /asterisk/config/dynamic/{configClass}/{objectType}/{id}.
+ * \param get_params GET parameters in the HTTP request.
+ * \param path_vars Path variables extracted from the request.
+ * \param headers HTTP headers.
+ * \param[out] response Response to the HTTP request.
+ */
+static void ast_ari_asterisk_get_object_cb(
+	struct ast_tcptls_session_instance *ser,
+	struct ast_variable *get_params, struct ast_variable *path_vars,
+	struct ast_variable *headers, struct ast_ari_response *response)
+{
+	struct ast_ari_asterisk_get_object_args args = {};
+	struct ast_variable *i;
+	RAII_VAR(struct ast_json *, body, NULL, ast_json_unref);
+#if defined(AST_DEVMODE)
+	int is_valid;
+	int code;
+#endif /* AST_DEVMODE */
+
+	for (i = path_vars; i; i = i->next) {
+		if (strcmp(i->name, "configClass") == 0) {
+			args.config_class = (i->value);
+		} else
+		if (strcmp(i->name, "objectType") == 0) {
+			args.object_type = (i->value);
+		} else
+		if (strcmp(i->name, "id") == 0) {
+			args.id = (i->value);
+		} else
+		{}
+	}
+	ast_ari_asterisk_get_object(headers, &args, response);
+#if defined(AST_DEVMODE)
+	code = response->response_code;
+
+	switch (code) {
+	case 0: /* Implementation is still a stub, or the code wasn't set */
+		is_valid = response->message == NULL;
+		break;
+	case 500: /* Internal Server Error */
+	case 501: /* Not Implemented */
+	case 404: /* {configClass|objectType|id} not found */
+		is_valid = 1;
+		break;
+	default:
+		if (200 <= code && code <= 299) {
+			is_valid = ast_ari_validate_list(response->message,
+				ast_ari_validate_config_tuple_fn());
+		} else {
+			ast_log(LOG_ERROR, "Invalid error response %d for /asterisk/config/dynamic/{configClass}/{objectType}/{id}\n", code);
+			is_valid = 0;
+		}
+	}
+
+	if (!is_valid) {
+		ast_log(LOG_ERROR, "Response validation failed for /asterisk/config/dynamic/{configClass}/{objectType}/{id}\n");
+		ast_ari_response_error(response, 500,
+			"Internal Server Error", "Response validation failed");
+	}
+#endif /* AST_DEVMODE */
+
+fin: __attribute__((unused))
+	return;
+}
+int ast_ari_asterisk_update_object_parse_body(
+	struct ast_json *body,
+	struct ast_ari_asterisk_update_object_args *args)
+{
+	/* Parse query parameters out of it */
+	return 0;
+}
+
+/*!
+ * \brief Parameter parsing callback for /asterisk/config/dynamic/{configClass}/{objectType}/{id}.
+ * \param get_params GET parameters in the HTTP request.
+ * \param path_vars Path variables extracted from the request.
+ * \param headers HTTP headers.
+ * \param[out] response Response to the HTTP request.
+ */
+static void ast_ari_asterisk_update_object_cb(
+	struct ast_tcptls_session_instance *ser,
+	struct ast_variable *get_params, struct ast_variable *path_vars,
+	struct ast_variable *headers, struct ast_ari_response *response)
+{
+	struct ast_ari_asterisk_update_object_args args = {};
+	struct ast_variable *i;
+	RAII_VAR(struct ast_json *, body, NULL, ast_json_unref);
+#if defined(AST_DEVMODE)
+	int is_valid;
+	int code;
+#endif /* AST_DEVMODE */
+
+	for (i = path_vars; i; i = i->next) {
+		if (strcmp(i->name, "configClass") == 0) {
+			args.config_class = (i->value);
+		} else
+		if (strcmp(i->name, "objectType") == 0) {
+			args.object_type = (i->value);
+		} else
+		if (strcmp(i->name, "id") == 0) {
+			args.id = (i->value);
+		} else
+		{}
+	}
+	/* Look for a JSON request entity */
+	body = ast_http_get_json(ser, headers);
+	if (!body) {
+		switch (errno) {
+		case EFBIG:
+			ast_ari_response_error(response, 413, "Request Entity Too Large", "Request body too large");
+			goto fin;
+		case ENOMEM:
+			ast_ari_response_error(response, 500, "Internal Server Error", "Error processing request");
+			goto fin;
+		case EIO:
+			ast_ari_response_error(response, 400, "Bad Request", "Error parsing request body");
+			goto fin;
+		}
+	}
+	args.fields = body;
+	ast_ari_asterisk_update_object(headers, &args, response);
+#if defined(AST_DEVMODE)
+	code = response->response_code;
+
+	switch (code) {
+	case 0: /* Implementation is still a stub, or the code wasn't set */
+		is_valid = response->message == NULL;
+		break;
+	case 500: /* Internal Server Error */
+	case 501: /* Not Implemented */
+	case 400: /* Bad request body */
+	case 403: /* Could not create or update object */
+	case 404: /* {configClass|objectType} not found */
+		is_valid = 1;
+		break;
+	default:
+		if (200 <= code && code <= 299) {
+			is_valid = ast_ari_validate_list(response->message,
+				ast_ari_validate_config_tuple_fn());
+		} else {
+			ast_log(LOG_ERROR, "Invalid error response %d for /asterisk/config/dynamic/{configClass}/{objectType}/{id}\n", code);
+			is_valid = 0;
+		}
+	}
+
+	if (!is_valid) {
+		ast_log(LOG_ERROR, "Response validation failed for /asterisk/config/dynamic/{configClass}/{objectType}/{id}\n");
+		ast_ari_response_error(response, 500,
+			"Internal Server Error", "Response validation failed");
+	}
+#endif /* AST_DEVMODE */
+
+fin: __attribute__((unused))
+	return;
+}
+/*!
+ * \brief Parameter parsing callback for /asterisk/config/dynamic/{configClass}/{objectType}/{id}.
+ * \param get_params GET parameters in the HTTP request.
+ * \param path_vars Path variables extracted from the request.
+ * \param headers HTTP headers.
+ * \param[out] response Response to the HTTP request.
+ */
+static void ast_ari_asterisk_delete_object_cb(
+	struct ast_tcptls_session_instance *ser,
+	struct ast_variable *get_params, struct ast_variable *path_vars,
+	struct ast_variable *headers, struct ast_ari_response *response)
+{
+	struct ast_ari_asterisk_delete_object_args args = {};
+	struct ast_variable *i;
+	RAII_VAR(struct ast_json *, body, NULL, ast_json_unref);
+#if defined(AST_DEVMODE)
+	int is_valid;
+	int code;
+#endif /* AST_DEVMODE */
+
+	for (i = path_vars; i; i = i->next) {
+		if (strcmp(i->name, "configClass") == 0) {
+			args.config_class = (i->value);
+		} else
+		if (strcmp(i->name, "objectType") == 0) {
+			args.object_type = (i->value);
+		} else
+		if (strcmp(i->name, "id") == 0) {
+			args.id = (i->value);
+		} else
+		{}
+	}
+	ast_ari_asterisk_delete_object(headers, &args, response);
+#if defined(AST_DEVMODE)
+	code = response->response_code;
+
+	switch (code) {
+	case 0: /* Implementation is still a stub, or the code wasn't set */
+		is_valid = response->message == NULL;
+		break;
+	case 500: /* Internal Server Error */
+	case 501: /* Not Implemented */
+	case 403: /* Could not delete object */
+	case 404: /* {configClass|objectType|id} not found */
+		is_valid = 1;
+		break;
+	default:
+		if (200 <= code && code <= 299) {
+			is_valid = ast_ari_validate_void(
+				response->message);
+		} else {
+			ast_log(LOG_ERROR, "Invalid error response %d for /asterisk/config/dynamic/{configClass}/{objectType}/{id}\n", code);
+			is_valid = 0;
+		}
+	}
+
+	if (!is_valid) {
+		ast_log(LOG_ERROR, "Response validation failed for /asterisk/config/dynamic/{configClass}/{objectType}/{id}\n");
+		ast_ari_response_error(response, 500,
+			"Internal Server Error", "Response validation failed");
+	}
+#endif /* AST_DEVMODE */
+
+fin: __attribute__((unused))
+	return;
+}
 int ast_ari_asterisk_get_info_parse_body(
 	struct ast_json *body,
-	struct ast_ari_asterisk_get_info_args *args)
+	struct ast_ari_asterisk_get_info_args *args)
+{
+	struct ast_json *field;
+	/* Parse query parameters out of it */
+	field = ast_json_object_get(body, "only");
+	if (field) {
+		/* If they were silly enough to both pass in a query param and a
+		 * JSON body, free up the query value.
+		 */
+		ast_free(args->only);
+		if (ast_json_typeof(field) == AST_JSON_ARRAY) {
+			/* Multiple param passed as array */
+			size_t i;
+			args->only_count = ast_json_array_size(field);
+			args->only = ast_malloc(sizeof(*args->only) * args->only_count);
+
+			if (!args->only) {
+				return -1;
+			}
+
+			for (i = 0; i < args->only_count; ++i) {
+				args->only[i] = ast_json_string_get(ast_json_array_get(field, i));
+			}
+		} else {
+			/* Multiple param passed as single value */
+			args->only_count = 1;
+			args->only = ast_malloc(sizeof(*args->only) * args->only_count);
+			if (!args->only) {
+				return -1;
+			}
+			args->only[0] = ast_json_string_get(field);
+		}
+	}
+	return 0;
+}
+
+/*!
+ * \brief Parameter parsing callback for /asterisk/info.
+ * \param get_params GET parameters in the HTTP request.
+ * \param path_vars Path variables extracted from the request.
+ * \param headers HTTP headers.
+ * \param[out] response Response to the HTTP request.
+ */
+static void ast_ari_asterisk_get_info_cb(
+	struct ast_tcptls_session_instance *ser,
+	struct ast_variable *get_params, struct ast_variable *path_vars,
+	struct ast_variable *headers, struct ast_ari_response *response)
+{
+	struct ast_ari_asterisk_get_info_args args = {};
+	struct ast_variable *i;
+	RAII_VAR(struct ast_json *, body, NULL, ast_json_unref);
+#if defined(AST_DEVMODE)
+	int is_valid;
+	int code;
+#endif /* AST_DEVMODE */
+
+	for (i = get_params; i; i = i->next) {
+		if (strcmp(i->name, "only") == 0) {
+			/* Parse comma separated list */
+			char *vals[MAX_VALS];
+			size_t j;
+
+			args.only_parse = ast_strdup(i->value);
+			if (!args.only_parse) {
+				ast_ari_response_alloc_failed(response);
+				goto fin;
+			}
+
+			if (strlen(args.only_parse) == 0) {
+				/* ast_app_separate_args can't handle "" */
+				args.only_count = 1;
+				vals[0] = args.only_parse;
+			} else {
+				args.only_count = ast_app_separate_args(
+					args.only_parse, ',', vals,
+					ARRAY_LEN(vals));
+			}
+
+			if (args.only_count == 0) {
+				ast_ari_response_alloc_failed(response);
+				goto fin;
+			}
+
+			if (args.only_count >= MAX_VALS) {
+				ast_ari_response_error(response, 400,
+					"Bad Request",
+					"Too many values for only");
+				goto fin;
+			}
+
+			args.only = ast_malloc(sizeof(*args.only) * args.only_count);
+			if (!args.only) {
+				ast_ari_response_alloc_failed(response);
+				goto fin;
+			}
+
+			for (j = 0; j < args.only_count; ++j) {
+				args.only[j] = (vals[j]);
+			}
+		} else
+		{}
+	}
+	/* Look for a JSON request entity */
+	body = ast_http_get_json(ser, headers);
+	if (!body) {
+		switch (errno) {
+		case EFBIG:
+			ast_ari_response_error(response, 413, "Request Entity Too Large", "Request body too large");
+			goto fin;
+		case ENOMEM:
+			ast_ari_response_error(response, 500, "Internal Server Error", "Error processing request");
+			goto fin;
+		case EIO:
+			ast_ari_response_error(response, 400, "Bad Request", "Error parsing request body");
+			goto fin;
+		}
+	}
+	if (ast_ari_asterisk_get_info_parse_body(body, &args)) {
+		ast_ari_response_alloc_failed(response);
+		goto fin;
+	}
+	ast_ari_asterisk_get_info(headers, &args, response);
+#if defined(AST_DEVMODE)
+	code = response->response_code;
+
+	switch (code) {
+	case 0: /* Implementation is still a stub, or the code wasn't set */
+		is_valid = response->message == NULL;
+		break;
+	case 500: /* Internal Server Error */
+	case 501: /* Not Implemented */
+		is_valid = 1;
+		break;
+	default:
+		if (200 <= code && code <= 299) {
+			is_valid = ast_ari_validate_asterisk_info(
+				response->message);
+		} else {
+			ast_log(LOG_ERROR, "Invalid error response %d for /asterisk/info\n", code);
+			is_valid = 0;
+		}
+	}
+
+	if (!is_valid) {
+		ast_log(LOG_ERROR, "Response validation failed for /asterisk/info\n");
+		ast_ari_response_error(response, 500,
+			"Internal Server Error", "Response validation failed");
+	}
+#endif /* AST_DEVMODE */
+
+fin: __attribute__((unused))
+	ast_free(args.only_parse);
+	ast_free(args.only);
+	return;
+}
+/*!
+ * \brief Parameter parsing callback for /asterisk/modules.
+ * \param get_params GET parameters in the HTTP request.
+ * \param path_vars Path variables extracted from the request.
+ * \param headers HTTP headers.
+ * \param[out] response Response to the HTTP request.
+ */
+static void ast_ari_asterisk_list_modules_cb(
+	struct ast_tcptls_session_instance *ser,
+	struct ast_variable *get_params, struct ast_variable *path_vars,
+	struct ast_variable *headers, struct ast_ari_response *response)
+{
+	struct ast_ari_asterisk_list_modules_args args = {};
+	RAII_VAR(struct ast_json *, body, NULL, ast_json_unref);
+#if defined(AST_DEVMODE)
+	int is_valid;
+	int code;
+#endif /* AST_DEVMODE */
+
+	ast_ari_asterisk_list_modules(headers, &args, response);
+#if defined(AST_DEVMODE)
+	code = response->response_code;
+
+	switch (code) {
+	case 0: /* Implementation is still a stub, or the code wasn't set */
+		is_valid = response->message == NULL;
+		break;
+	case 500: /* Internal Server Error */
+	case 501: /* Not Implemented */
+		is_valid = 1;
+		break;
+	default:
+		if (200 <= code && code <= 299) {
+			is_valid = ast_ari_validate_list(response->message,
+				ast_ari_validate_module_fn());
+		} else {
+			ast_log(LOG_ERROR, "Invalid error response %d for /asterisk/modules\n", code);
+			is_valid = 0;
+		}
+	}
+
+	if (!is_valid) {
+		ast_log(LOG_ERROR, "Response validation failed for /asterisk/modules\n");
+		ast_ari_response_error(response, 500,
+			"Internal Server Error", "Response validation failed");
+	}
+#endif /* AST_DEVMODE */
+
+fin: __attribute__((unused))
+	return;
+}
+/*!
+ * \brief Parameter parsing callback for /asterisk/modules/{moduleName}.
+ * \param get_params GET parameters in the HTTP request.
+ * \param path_vars Path variables extracted from the request.
+ * \param headers HTTP headers.
+ * \param[out] response Response to the HTTP request.
+ */
+static void ast_ari_asterisk_get_module_cb(
+	struct ast_tcptls_session_instance *ser,
+	struct ast_variable *get_params, struct ast_variable *path_vars,
+	struct ast_variable *headers, struct ast_ari_response *response)
+{
+	struct ast_ari_asterisk_get_module_args args = {};
+	struct ast_variable *i;
+	RAII_VAR(struct ast_json *, body, NULL, ast_json_unref);
+#if defined(AST_DEVMODE)
+	int is_valid;
+	int code;
+#endif /* AST_DEVMODE */
+
+	for (i = path_vars; i; i = i->next) {
+		if (strcmp(i->name, "moduleName") == 0) {
+			args.module_name = (i->value);
+		} else
+		{}
+	}
+	ast_ari_asterisk_get_module(headers, &args, response);
+#if defined(AST_DEVMODE)
+	code = response->response_code;
+
+	switch (code) {
+	case 0: /* Implementation is still a stub, or the code wasn't set */
+		is_valid = response->message == NULL;
+		break;
+	case 500: /* Internal Server Error */
+	case 501: /* Not Implemented */
+	case 404: /* Module could not be found in running modules. */
+	case 409: /* Module information could not be retrieved. */
+		is_valid = 1;
+		break;
+	default:
+		if (200 <= code && code <= 299) {
+			is_valid = ast_ari_validate_module(
+				response->message);
+		} else {
+			ast_log(LOG_ERROR, "Invalid error response %d for /asterisk/modules/{moduleName}\n", code);
+			is_valid = 0;
+		}
+	}
+
+	if (!is_valid) {
+		ast_log(LOG_ERROR, "Response validation failed for /asterisk/modules/{moduleName}\n");
+		ast_ari_response_error(response, 500,
+			"Internal Server Error", "Response validation failed");
+	}
+#endif /* AST_DEVMODE */
+
+fin: __attribute__((unused))
+	return;
+}
+/*!
+ * \brief Parameter parsing callback for /asterisk/modules/{moduleName}.
+ * \param get_params GET parameters in the HTTP request.
+ * \param path_vars Path variables extracted from the request.
+ * \param headers HTTP headers.
+ * \param[out] response Response to the HTTP request.
+ */
+static void ast_ari_asterisk_load_module_cb(
+	struct ast_tcptls_session_instance *ser,
+	struct ast_variable *get_params, struct ast_variable *path_vars,
+	struct ast_variable *headers, struct ast_ari_response *response)
+{
+	struct ast_ari_asterisk_load_module_args args = {};
+	struct ast_variable *i;
+	RAII_VAR(struct ast_json *, body, NULL, ast_json_unref);
+#if defined(AST_DEVMODE)
+	int is_valid;
+	int code;
+#endif /* AST_DEVMODE */
+
+	for (i = path_vars; i; i = i->next) {
+		if (strcmp(i->name, "moduleName") == 0) {
+			args.module_name = (i->value);
+		} else
+		{}
+	}
+	ast_ari_asterisk_load_module(headers, &args, response);
+#if defined(AST_DEVMODE)
+	code = response->response_code;
+
+	switch (code) {
+	case 0: /* Implementation is still a stub, or the code wasn't set */
+		is_valid = response->message == NULL;
+		break;
+	case 500: /* Internal Server Error */
+	case 501: /* Not Implemented */
+	case 409: /* Module could not be loaded. */
+		is_valid = 1;
+		break;
+	default:
+		if (200 <= code && code <= 299) {
+			is_valid = ast_ari_validate_void(
+				response->message);
+		} else {
+			ast_log(LOG_ERROR, "Invalid error response %d for /asterisk/modules/{moduleName}\n", code);
+			is_valid = 0;
+		}
+	}
+
+	if (!is_valid) {
+		ast_log(LOG_ERROR, "Response validation failed for /asterisk/modules/{moduleName}\n");
+		ast_ari_response_error(response, 500,
+			"Internal Server Error", "Response validation failed");
+	}
+#endif /* AST_DEVMODE */
+
+fin: __attribute__((unused))
+	return;
+}
+/*!
+ * \brief Parameter parsing callback for /asterisk/modules/{moduleName}.
+ * \param get_params GET parameters in the HTTP request.
+ * \param path_vars Path variables extracted from the request.
+ * \param headers HTTP headers.
+ * \param[out] response Response to the HTTP request.
+ */
+static void ast_ari_asterisk_unload_module_cb(
+	struct ast_tcptls_session_instance *ser,
+	struct ast_variable *get_params, struct ast_variable *path_vars,
+	struct ast_variable *headers, struct ast_ari_response *response)
+{
+	struct ast_ari_asterisk_unload_module_args args = {};
+	struct ast_variable *i;
+	RAII_VAR(struct ast_json *, body, NULL, ast_json_unref);
+#if defined(AST_DEVMODE)
+	int is_valid;
+	int code;
+#endif /* AST_DEVMODE */
+
+	for (i = path_vars; i; i = i->next) {
+		if (strcmp(i->name, "moduleName") == 0) {
+			args.module_name = (i->value);
+		} else
+		{}
+	}
+	ast_ari_asterisk_unload_module(headers, &args, response);
+#if defined(AST_DEVMODE)
+	code = response->response_code;
+
+	switch (code) {
+	case 0: /* Implementation is still a stub, or the code wasn't set */
+		is_valid = response->message == NULL;
+		break;
+	case 500: /* Internal Server Error */
+	case 501: /* Not Implemented */
+	case 404: /* Module not found in running modules. */
+	case 409: /* Module could not be unloaded. */
+		is_valid = 1;
+		break;
+	default:
+		if (200 <= code && code <= 299) {
+			is_valid = ast_ari_validate_void(
+				response->message);
+		} else {
+			ast_log(LOG_ERROR, "Invalid error response %d for /asterisk/modules/{moduleName}\n", code);
+			is_valid = 0;
+		}
+	}
+
+	if (!is_valid) {
+		ast_log(LOG_ERROR, "Response validation failed for /asterisk/modules/{moduleName}\n");
+		ast_ari_response_error(response, 500,
+			"Internal Server Error", "Response validation failed");
+	}
+#endif /* AST_DEVMODE */
+
+fin: __attribute__((unused))
+	return;
+}
+/*!
+ * \brief Parameter parsing callback for /asterisk/modules/{moduleName}.
+ * \param get_params GET parameters in the HTTP request.
+ * \param path_vars Path variables extracted from the request.
+ * \param headers HTTP headers.
+ * \param[out] response Response to the HTTP request.
+ */
+static void ast_ari_asterisk_reload_module_cb(
+	struct ast_tcptls_session_instance *ser,
+	struct ast_variable *get_params, struct ast_variable *path_vars,
+	struct ast_variable *headers, struct ast_ari_response *response)
+{
+	struct ast_ari_asterisk_reload_module_args args = {};
+	struct ast_variable *i;
+	RAII_VAR(struct ast_json *, body, NULL, ast_json_unref);
+#if defined(AST_DEVMODE)
+	int is_valid;
+	int code;
+#endif /* AST_DEVMODE */
+
+	for (i = path_vars; i; i = i->next) {
+		if (strcmp(i->name, "moduleName") == 0) {
+			args.module_name = (i->value);
+		} else
+		{}
+	}
+	ast_ari_asterisk_reload_module(headers, &args, response);
+#if defined(AST_DEVMODE)
+	code = response->response_code;
+
+	switch (code) {
+	case 0: /* Implementation is still a stub, or the code wasn't set */
+		is_valid = response->message == NULL;
+		break;
+	case 500: /* Internal Server Error */
+	case 501: /* Not Implemented */
+	case 404: /* Module not found in running modules. */
+	case 409: /* Module could not be reloaded. */
+		is_valid = 1;
+		break;
+	default:
+		if (200 <= code && code <= 299) {
+			is_valid = ast_ari_validate_void(
+				response->message);
+		} else {
+			ast_log(LOG_ERROR, "Invalid error response %d for /asterisk/modules/{moduleName}\n", code);
+			is_valid = 0;
+		}
+	}
+
+	if (!is_valid) {
+		ast_log(LOG_ERROR, "Response validation failed for /asterisk/modules/{moduleName}\n");
+		ast_ari_response_error(response, 500,
+			"Internal Server Error", "Response validation failed");
+	}
+#endif /* AST_DEVMODE */
+
+fin: __attribute__((unused))
+	return;
+}
+/*!
+ * \brief Parameter parsing callback for /asterisk/logging.
+ * \param get_params GET parameters in the HTTP request.
+ * \param path_vars Path variables extracted from the request.
+ * \param headers HTTP headers.
+ * \param[out] response Response to the HTTP request.
+ */
+static void ast_ari_asterisk_list_log_channels_cb(
+	struct ast_tcptls_session_instance *ser,
+	struct ast_variable *get_params, struct ast_variable *path_vars,
+	struct ast_variable *headers, struct ast_ari_response *response)
+{
+	struct ast_ari_asterisk_list_log_channels_args args = {};
+	RAII_VAR(struct ast_json *, body, NULL, ast_json_unref);
+#if defined(AST_DEVMODE)
+	int is_valid;
+	int code;
+#endif /* AST_DEVMODE */
+
+	ast_ari_asterisk_list_log_channels(headers, &args, response);
+#if defined(AST_DEVMODE)
+	code = response->response_code;
+
+	switch (code) {
+	case 0: /* Implementation is still a stub, or the code wasn't set */
+		is_valid = response->message == NULL;
+		break;
+	case 500: /* Internal Server Error */
+	case 501: /* Not Implemented */
+		is_valid = 1;
+		break;
+	default:
+		if (200 <= code && code <= 299) {
+			is_valid = ast_ari_validate_list(response->message,
+				ast_ari_validate_log_channel_fn());
+		} else {
+			ast_log(LOG_ERROR, "Invalid error response %d for /asterisk/logging\n", code);
+			is_valid = 0;
+		}
+	}
+
+	if (!is_valid) {
+		ast_log(LOG_ERROR, "Response validation failed for /asterisk/logging\n");
+		ast_ari_response_error(response, 500,
+			"Internal Server Error", "Response validation failed");
+	}
+#endif /* AST_DEVMODE */
+
+fin: __attribute__((unused))
+	return;
+}
+int ast_ari_asterisk_add_log_parse_body(
+	struct ast_json *body,
+	struct ast_ari_asterisk_add_log_args *args)
 {
 	struct ast_json *field;
 	/* Parse query parameters out of it */
-	field = ast_json_object_get(body, "only");
+	field = ast_json_object_get(body, "configuration");
 	if (field) {
-		/* If they were silly enough to both pass in a query param and a
-		 * JSON body, free up the query value.
-		 */
-		ast_free(args->only);
-		if (ast_json_typeof(field) == AST_JSON_ARRAY) {
-			/* Multiple param passed as array */
-			size_t i;
-			args->only_count = ast_json_array_size(field);
-			args->only = ast_malloc(sizeof(*args->only) * args->only_count);
-
-			if (!args->only) {
-				return -1;
-			}
-
-			for (i = 0; i < args->only_count; ++i) {
-				args->only[i] = ast_json_string_get(ast_json_array_get(field, i));
-			}
-		} else {
-			/* Multiple param passed as single value */
-			args->only_count = 1;
-			args->only = ast_malloc(sizeof(*args->only) * args->only_count);
-			if (!args->only) {
-				return -1;
-			}
-			args->only[0] = ast_json_string_get(field);
-		}
+		args->configuration = ast_json_string_get(field);
 	}
 	return 0;
 }
 
 /*!
- * \brief Parameter parsing callback for /asterisk/info.
+ * \brief Parameter parsing callback for /asterisk/logging/{logChannelName}.
  * \param get_params GET parameters in the HTTP request.
  * \param path_vars Path variables extracted from the request.
  * \param headers HTTP headers.
  * \param[out] response Response to the HTTP request.
  */
-static void ast_ari_asterisk_get_info_cb(
+static void ast_ari_asterisk_add_log_cb(
 	struct ast_tcptls_session_instance *ser,
 	struct ast_variable *get_params, struct ast_variable *path_vars,
 	struct ast_variable *headers, struct ast_ari_response *response)
 {
-	struct ast_ari_asterisk_get_info_args args = {};
+	struct ast_ari_asterisk_add_log_args args = {};
 	struct ast_variable *i;
 	RAII_VAR(struct ast_json *, body, NULL, ast_json_unref);
 #if defined(AST_DEVMODE)
@@ -110,48 +806,14 @@ static void ast_ari_asterisk_get_info_cb(
 #endif /* AST_DEVMODE */
 
 	for (i = get_params; i; i = i->next) {
-		if (strcmp(i->name, "only") == 0) {
-			/* Parse comma separated list */
-			char *vals[MAX_VALS];
-			size_t j;
-
-			args.only_parse = ast_strdup(i->value);
-			if (!args.only_parse) {
-				ast_ari_response_alloc_failed(response);
-				goto fin;
-			}
-
-			if (strlen(args.only_parse) == 0) {
-				/* ast_app_separate_args can't handle "" */
-				args.only_count = 1;
-				vals[0] = args.only_parse;
-			} else {
-				args.only_count = ast_app_separate_args(
-					args.only_parse, ',', vals,
-					ARRAY_LEN(vals));
-			}
-
-			if (args.only_count == 0) {
-				ast_ari_response_alloc_failed(response);
-				goto fin;
-			}
-
-			if (args.only_count >= MAX_VALS) {
-				ast_ari_response_error(response, 400,
-					"Bad Request",
-					"Too many values for only");
-				goto fin;
-			}
-
-			args.only = ast_malloc(sizeof(*args.only) * args.only_count);
-			if (!args.only) {
-				ast_ari_response_alloc_failed(response);
-				goto fin;
-			}
-
-			for (j = 0; j < args.only_count; ++j) {
-				args.only[j] = (vals[j]);
-			}
+		if (strcmp(i->name, "configuration") == 0) {
+			args.configuration = (i->value);
+		} else
+		{}
+	}
+	for (i = path_vars; i; i = i->next) {
+		if (strcmp(i->name, "logChannelName") == 0) {
+			args.log_channel_name = (i->value);
 		} else
 		{}
 	}
@@ -170,11 +832,11 @@ static void ast_ari_asterisk_get_info_cb(
 			goto fin;
 		}
 	}
-	if (ast_ari_asterisk_get_info_parse_body(body, &args)) {
+	if (ast_ari_asterisk_add_log_parse_body(body, &args)) {
 		ast_ari_response_alloc_failed(response);
 		goto fin;
 	}
-	ast_ari_asterisk_get_info(headers, &args, response);
+	ast_ari_asterisk_add_log(headers, &args, response);
 #if defined(AST_DEVMODE)
 	code = response->response_code;
 
@@ -184,28 +846,146 @@ static void ast_ari_asterisk_get_info_cb(
 		break;
 	case 500: /* Internal Server Error */
 	case 501: /* Not Implemented */
+	case 400: /* Bad request body */
+	case 409: /* Log channel could not be created. */
 		is_valid = 1;
 		break;
 	default:
 		if (200 <= code && code <= 299) {
-			is_valid = ast_ari_validate_asterisk_info(
+			is_valid = ast_ari_validate_void(
 				response->message);
 		} else {
-			ast_log(LOG_ERROR, "Invalid error response %d for /asterisk/info\n", code);
+			ast_log(LOG_ERROR, "Invalid error response %d for /asterisk/logging/{logChannelName}\n", code);
 			is_valid = 0;
 		}
 	}
 
 	if (!is_valid) {
-		ast_log(LOG_ERROR, "Response validation failed for /asterisk/info\n");
+		ast_log(LOG_ERROR, "Response validation failed for /asterisk/logging/{logChannelName}\n");
+		ast_ari_response_error(response, 500,
+			"Internal Server Error", "Response validation failed");
+	}
+#endif /* AST_DEVMODE */
+
+fin: __attribute__((unused))
+	return;
+}
+/*!
+ * \brief Parameter parsing callback for /asterisk/logging/{logChannelName}.
+ * \param get_params GET parameters in the HTTP request.
+ * \param path_vars Path variables extracted from the request.
+ * \param headers HTTP headers.
+ * \param[out] response Response to the HTTP request.
+ */
+static void ast_ari_asterisk_delete_log_cb(
+	struct ast_tcptls_session_instance *ser,
+	struct ast_variable *get_params, struct ast_variable *path_vars,
+	struct ast_variable *headers, struct ast_ari_response *response)
+{
+	struct ast_ari_asterisk_delete_log_args args = {};
+	struct ast_variable *i;
+	RAII_VAR(struct ast_json *, body, NULL, ast_json_unref);
+#if defined(AST_DEVMODE)
+	int is_valid;
+	int code;
+#endif /* AST_DEVMODE */
+
+	for (i = path_vars; i; i = i->next) {
+		if (strcmp(i->name, "logChannelName") == 0) {
+			args.log_channel_name = (i->value);
+		} else
+		{}
+	}
+	ast_ari_asterisk_delete_log(headers, &args, response);
+#if defined(AST_DEVMODE)
+	code = response->response_code;
+
+	switch (code) {
+	case 0: /* Implementation is still a stub, or the code wasn't set */
+		is_valid = response->message == NULL;
+		break;
+	case 500: /* Internal Server Error */
+	case 501: /* Not Implemented */
+	case 404: /* Log channel does not exist. */
+		is_valid = 1;
+		break;
+	default:
+		if (200 <= code && code <= 299) {
+			is_valid = ast_ari_validate_void(
+				response->message);
+		} else {
+			ast_log(LOG_ERROR, "Invalid error response %d for /asterisk/logging/{logChannelName}\n", code);
+			is_valid = 0;
+		}
+	}
+
+	if (!is_valid) {
+		ast_log(LOG_ERROR, "Response validation failed for /asterisk/logging/{logChannelName}\n");
+		ast_ari_response_error(response, 500,
+			"Internal Server Error", "Response validation failed");
+	}
+#endif /* AST_DEVMODE */
+
+fin: __attribute__((unused))
+	return;
+}
+/*!
+ * \brief Parameter parsing callback for /asterisk/logging/{logChannelName}/rotate.
+ * \param get_params GET parameters in the HTTP request.
+ * \param path_vars Path variables extracted from the request.
+ * \param headers HTTP headers.
+ * \param[out] response Response to the HTTP request.
+ */
+static void ast_ari_asterisk_rotate_log_cb(
+	struct ast_tcptls_session_instance *ser,
+	struct ast_variable *get_params, struct ast_variable *path_vars,
+	struct ast_variable *headers, struct ast_ari_response *response)
+{
+	struct ast_ari_asterisk_rotate_log_args args = {};
+	struct ast_variable *i;
+	RAII_VAR(struct ast_json *, body, NULL, ast_json_unref);
+#if defined(AST_DEVMODE)
+	int is_valid;
+	int code;
+#endif /* AST_DEVMODE */
+
+	for (i = path_vars; i; i = i->next) {
+		if (strcmp(i->name, "logChannelName") == 0) {
+			args.log_channel_name = (i->value);
+		} else
+		{}
+	}
+	ast_ari_asterisk_rotate_log(headers, &args, response);
+#if defined(AST_DEVMODE)
+	code = response->response_code;
+
+	switch (code) {
+	case 0: /* Implementation is still a stub, or the code wasn't set */
+		is_valid = response->message == NULL;
+		break;
+	case 500: /* Internal Server Error */
+	case 501: /* Not Implemented */
+	case 404: /* Log channel does not exist. */
+		is_valid = 1;
+		break;
+	default:
+		if (200 <= code && code <= 299) {
+			is_valid = ast_ari_validate_void(
+				response->message);
+		} else {
+			ast_log(LOG_ERROR, "Invalid error response %d for /asterisk/logging/{logChannelName}/rotate\n", code);
+			is_valid = 0;
+		}
+	}
+
+	if (!is_valid) {
+		ast_log(LOG_ERROR, "Response validation failed for /asterisk/logging/{logChannelName}/rotate\n");
 		ast_ari_response_error(response, 500,
 			"Internal Server Error", "Response validation failed");
 	}
 #endif /* AST_DEVMODE */
 
 fin: __attribute__((unused))
-	ast_free(args.only_parse);
-	ast_free(args.only);
 	return;
 }
 int ast_ari_asterisk_get_global_var_parse_body(
@@ -399,6 +1179,52 @@ fin: __attribute__((unused))
 }
 
 /*! \brief REST handler for /api-docs/asterisk.{format} */
+static struct stasis_rest_handlers asterisk_config_dynamic_configClass_objectType_id = {
+	.path_segment = "id",
+	.is_wildcard = 1,
+	.callbacks = {
+		[AST_HTTP_GET] = ast_ari_asterisk_get_object_cb,
+		[AST_HTTP_PUT] = ast_ari_asterisk_update_object_cb,
+		[AST_HTTP_DELETE] = ast_ari_asterisk_delete_object_cb,
+	},
+	.num_children = 0,
+	.children = {  }
+};
+/*! \brief REST handler for /api-docs/asterisk.{format} */
+static struct stasis_rest_handlers asterisk_config_dynamic_configClass_objectType = {
+	.path_segment = "objectType",
+	.is_wildcard = 1,
+	.callbacks = {
+	},
+	.num_children = 1,
+	.children = { &asterisk_config_dynamic_configClass_objectType_id, }
+};
+/*! \brief REST handler for /api-docs/asterisk.{format} */
+static struct stasis_rest_handlers asterisk_config_dynamic_configClass = {
+	.path_segment = "configClass",
+	.is_wildcard = 1,
+	.callbacks = {
+	},
+	.num_children = 1,
+	.children = { &asterisk_config_dynamic_configClass_objectType, }
+};
+/*! \brief REST handler for /api-docs/asterisk.{format} */
+static struct stasis_rest_handlers asterisk_config_dynamic = {
+	.path_segment = "dynamic",
+	.callbacks = {
+	},
+	.num_children = 1,
+	.children = { &asterisk_config_dynamic_configClass, }
+};
+/*! \brief REST handler for /api-docs/asterisk.{format} */
+static struct stasis_rest_handlers asterisk_config = {
+	.path_segment = "config",
+	.callbacks = {
+	},
+	.num_children = 1,
+	.children = { &asterisk_config_dynamic, }
+};
+/*! \brief REST handler for /api-docs/asterisk.{format} */
 static struct stasis_rest_handlers asterisk_info = {
 	.path_segment = "info",
 	.callbacks = {
@@ -408,6 +1234,57 @@ static struct stasis_rest_handlers asterisk_info = {
 	.children = {  }
 };
 /*! \brief REST handler for /api-docs/asterisk.{format} */
+static struct stasis_rest_handlers asterisk_modules_moduleName = {
+	.path_segment = "moduleName",
+	.is_wildcard = 1,
+	.callbacks = {
+		[AST_HTTP_GET] = ast_ari_asterisk_get_module_cb,
+		[AST_HTTP_POST] = ast_ari_asterisk_load_module_cb,
+		[AST_HTTP_DELETE] = ast_ari_asterisk_unload_module_cb,
+		[AST_HTTP_PUT] = ast_ari_asterisk_reload_module_cb,
+	},
+	.num_children = 0,
+	.children = {  }
+};
+/*! \brief REST handler for /api-docs/asterisk.{format} */
+static struct stasis_rest_handlers asterisk_modules = {
+	.path_segment = "modules",
+	.callbacks = {
+		[AST_HTTP_GET] = ast_ari_asterisk_list_modules_cb,
+	},
+	.num_children = 1,
+	.children = { &asterisk_modules_moduleName, }
+};
+/*! \brief REST handler for /api-docs/asterisk.{format} */
+static struct stasis_rest_handlers asterisk_logging_logChannelName_rotate = {
+	.path_segment = "rotate",
+	.callbacks = {
+		[AST_HTTP_PUT] = ast_ari_asterisk_rotate_log_cb,
+	},
+	.num_children = 0,
+	.children = {  }
+};
+/*! \brief REST handler for /api-docs/asterisk.{format} */
+static struct stasis_rest_handlers asterisk_logging_logChannelName = {
+	.path_segment = "logChannelName",
+	.is_wildcard = 1,
+	.callbacks = {
+		[AST_HTTP_POST] = ast_ari_asterisk_add_log_cb,
+		[AST_HTTP_DELETE] = ast_ari_asterisk_delete_log_cb,
+	},
+	.num_children = 1,
+	.children = { &asterisk_logging_logChannelName_rotate, }
+};
+/*! \brief REST handler for /api-docs/asterisk.{format} */
+static struct stasis_rest_handlers asterisk_logging = {
+	.path_segment = "logging",
+	.callbacks = {
+		[AST_HTTP_GET] = ast_ari_asterisk_list_log_channels_cb,
+	},
+	.num_children = 1,
+	.children = { &asterisk_logging_logChannelName, }
+};
+/*! \brief REST handler for /api-docs/asterisk.{format} */
 static struct stasis_rest_handlers asterisk_variable = {
 	.path_segment = "variable",
 	.callbacks = {
@@ -422,8 +1299,8 @@ static struct stasis_rest_handlers asterisk = {
 	.path_segment = "asterisk",
 	.callbacks = {
 	},
-	.num_children = 2,
-	.children = { &asterisk_info,&asterisk_variable, }
+	.num_children = 5,
+	.children = { &asterisk_config,&asterisk_info,&asterisk_modules,&asterisk_logging,&asterisk_variable, }
 };
 
 static int load_module(void)
diff --git a/res/res_ari_bridges.c b/res/res_ari_bridges.c
index 72d33c6..860ff1d 100644
--- a/res/res_ari_bridges.c
+++ b/res/res_ari_bridges.c
@@ -33,13 +33,14 @@
 
 /*** MODULEINFO
 	<depend type="module">res_ari</depend>
+	<depend type="module">res_ari_model</depend>
 	<depend type="module">res_stasis</depend>
 	<support_level>core</support_level>
  ***/
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/app.h"
 #include "asterisk/module.h"
@@ -206,9 +207,9 @@ static void ast_ari_bridges_create_cb(
 fin: __attribute__((unused))
 	return;
 }
-int ast_ari_bridges_create_or_update_with_id_parse_body(
+int ast_ari_bridges_create_with_id_parse_body(
 	struct ast_json *body,
-	struct ast_ari_bridges_create_or_update_with_id_args *args)
+	struct ast_ari_bridges_create_with_id_args *args)
 {
 	struct ast_json *field;
 	/* Parse query parameters out of it */
@@ -230,12 +231,12 @@ int ast_ari_bridges_create_or_update_with_id_parse_body(
  * \param headers HTTP headers.
  * \param[out] response Response to the HTTP request.
  */
-static void ast_ari_bridges_create_or_update_with_id_cb(
+static void ast_ari_bridges_create_with_id_cb(
 	struct ast_tcptls_session_instance *ser,
 	struct ast_variable *get_params, struct ast_variable *path_vars,
 	struct ast_variable *headers, struct ast_ari_response *response)
 {
-	struct ast_ari_bridges_create_or_update_with_id_args args = {};
+	struct ast_ari_bridges_create_with_id_args args = {};
 	struct ast_variable *i;
 	RAII_VAR(struct ast_json *, body, NULL, ast_json_unref);
 #if defined(AST_DEVMODE)
@@ -273,11 +274,11 @@ static void ast_ari_bridges_create_or_update_with_id_cb(
 			goto fin;
 		}
 	}
-	if (ast_ari_bridges_create_or_update_with_id_parse_body(body, &args)) {
+	if (ast_ari_bridges_create_with_id_parse_body(body, &args)) {
 		ast_ari_response_alloc_failed(response);
 		goto fin;
 	}
-	ast_ari_bridges_create_or_update_with_id(headers, &args, response);
+	ast_ari_bridges_create_with_id(headers, &args, response);
 #if defined(AST_DEVMODE)
 	code = response->response_code;
 
@@ -1378,7 +1379,7 @@ static struct stasis_rest_handlers bridges_bridgeId = {
 	.path_segment = "bridgeId",
 	.is_wildcard = 1,
 	.callbacks = {
-		[AST_HTTP_POST] = ast_ari_bridges_create_or_update_with_id_cb,
+		[AST_HTTP_POST] = ast_ari_bridges_create_with_id_cb,
 		[AST_HTTP_GET] = ast_ari_bridges_get_cb,
 		[AST_HTTP_DELETE] = ast_ari_bridges_destroy_cb,
 	},
diff --git a/res/res_ari_channels.c b/res/res_ari_channels.c
index 17538e5..d1ae801 100644
--- a/res/res_ari_channels.c
+++ b/res/res_ari_channels.c
@@ -33,13 +33,14 @@
 
 /*** MODULEINFO
 	<depend type="module">res_ari</depend>
+	<depend type="module">res_ari_model</depend>
 	<depend type="module">res_stasis</depend>
 	<support_level>core</support_level>
  ***/
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/app.h"
 #include "asterisk/module.h"
@@ -124,6 +125,10 @@ int ast_ari_channels_originate_parse_body(
 	if (field) {
 		args->priority = ast_json_integer_get(field);
 	}
+	field = ast_json_object_get(body, "label");
+	if (field) {
+		args->label = ast_json_string_get(field);
+	}
 	field = ast_json_object_get(body, "app");
 	if (field) {
 		args->app = ast_json_string_get(field);
@@ -148,6 +153,10 @@ int ast_ari_channels_originate_parse_body(
 	if (field) {
 		args->other_channel_id = ast_json_string_get(field);
 	}
+	field = ast_json_object_get(body, "originator");
+	if (field) {
+		args->originator = ast_json_string_get(field);
+	}
 	return 0;
 }
 
@@ -184,6 +193,9 @@ static void ast_ari_channels_originate_cb(
 		if (strcmp(i->name, "priority") == 0) {
 			args.priority = atol(i->value);
 		} else
+		if (strcmp(i->name, "label") == 0) {
+			args.label = (i->value);
+		} else
 		if (strcmp(i->name, "app") == 0) {
 			args.app = (i->value);
 		} else
@@ -202,6 +214,9 @@ static void ast_ari_channels_originate_cb(
 		if (strcmp(i->name, "otherChannelId") == 0) {
 			args.other_channel_id = (i->value);
 		} else
+		if (strcmp(i->name, "originator") == 0) {
+			args.originator = (i->value);
+		} else
 		{}
 	}
 	/* Look for a JSON request entity */
@@ -219,7 +234,7 @@ static void ast_ari_channels_originate_cb(
 			goto fin;
 		}
 	}
-	args.variables = ast_json_ref(body);
+	args.variables = body;
 	ast_ari_channels_originate(headers, &args, response);
 #if defined(AST_DEVMODE)
 	code = response->response_code;
@@ -334,6 +349,10 @@ int ast_ari_channels_originate_with_id_parse_body(
 	if (field) {
 		args->priority = ast_json_integer_get(field);
 	}
+	field = ast_json_object_get(body, "label");
+	if (field) {
+		args->label = ast_json_string_get(field);
+	}
 	field = ast_json_object_get(body, "app");
 	if (field) {
 		args->app = ast_json_string_get(field);
@@ -354,6 +373,10 @@ int ast_ari_channels_originate_with_id_parse_body(
 	if (field) {
 		args->other_channel_id = ast_json_string_get(field);
 	}
+	field = ast_json_object_get(body, "originator");
+	if (field) {
+		args->originator = ast_json_string_get(field);
+	}
 	return 0;
 }
 
@@ -390,6 +413,9 @@ static void ast_ari_channels_originate_with_id_cb(
 		if (strcmp(i->name, "priority") == 0) {
 			args.priority = atol(i->value);
 		} else
+		if (strcmp(i->name, "label") == 0) {
+			args.label = (i->value);
+		} else
 		if (strcmp(i->name, "app") == 0) {
 			args.app = (i->value);
 		} else
@@ -405,6 +431,9 @@ static void ast_ari_channels_originate_with_id_cb(
 		if (strcmp(i->name, "otherChannelId") == 0) {
 			args.other_channel_id = (i->value);
 		} else
+		if (strcmp(i->name, "originator") == 0) {
+			args.originator = (i->value);
+		} else
 		{}
 	}
 	for (i = path_vars; i; i = i->next) {
@@ -428,7 +457,7 @@ static void ast_ari_channels_originate_with_id_cb(
 			goto fin;
 		}
 	}
-	args.variables = ast_json_ref(body);
+	args.variables = body;
 	ast_ari_channels_originate_with_id(headers, &args, response);
 #if defined(AST_DEVMODE)
 	code = response->response_code;
@@ -578,6 +607,10 @@ int ast_ari_channels_continue_in_dialplan_parse_body(
 	if (field) {
 		args->priority = ast_json_integer_get(field);
 	}
+	field = ast_json_object_get(body, "label");
+	if (field) {
+		args->label = ast_json_string_get(field);
+	}
 	return 0;
 }
 
@@ -611,6 +644,9 @@ static void ast_ari_channels_continue_in_dialplan_cb(
 		if (strcmp(i->name, "priority") == 0) {
 			args.priority = atoi(i->value);
 		} else
+		if (strcmp(i->name, "label") == 0) {
+			args.label = (i->value);
+		} else
 		{}
 	}
 	for (i = path_vars; i; i = i->next) {
@@ -672,6 +708,106 @@ static void ast_ari_channels_continue_in_dialplan_cb(
 fin: __attribute__((unused))
 	return;
 }
+int ast_ari_channels_redirect_parse_body(
+	struct ast_json *body,
+	struct ast_ari_channels_redirect_args *args)
+{
+	struct ast_json *field;
+	/* Parse query parameters out of it */
+	field = ast_json_object_get(body, "endpoint");
+	if (field) {
+		args->endpoint = ast_json_string_get(field);
+	}
+	return 0;
+}
+
+/*!
+ * \brief Parameter parsing callback for /channels/{channelId}/redirect.
+ * \param get_params GET parameters in the HTTP request.
+ * \param path_vars Path variables extracted from the request.
+ * \param headers HTTP headers.
+ * \param[out] response Response to the HTTP request.
+ */
+static void ast_ari_channels_redirect_cb(
+	struct ast_tcptls_session_instance *ser,
+	struct ast_variable *get_params, struct ast_variable *path_vars,
+	struct ast_variable *headers, struct ast_ari_response *response)
+{
+	struct ast_ari_channels_redirect_args args = {};
+	struct ast_variable *i;
+	RAII_VAR(struct ast_json *, body, NULL, ast_json_unref);
+#if defined(AST_DEVMODE)
+	int is_valid;
+	int code;
+#endif /* AST_DEVMODE */
+
+	for (i = get_params; i; i = i->next) {
+		if (strcmp(i->name, "endpoint") == 0) {
+			args.endpoint = (i->value);
+		} else
+		{}
+	}
+	for (i = path_vars; i; i = i->next) {
+		if (strcmp(i->name, "channelId") == 0) {
+			args.channel_id = (i->value);
+		} else
+		{}
+	}
+	/* Look for a JSON request entity */
+	body = ast_http_get_json(ser, headers);
+	if (!body) {
+		switch (errno) {
+		case EFBIG:
+			ast_ari_response_error(response, 413, "Request Entity Too Large", "Request body too large");
+			goto fin;
+		case ENOMEM:
+			ast_ari_response_error(response, 500, "Internal Server Error", "Error processing request");
+			goto fin;
+		case EIO:
+			ast_ari_response_error(response, 400, "Bad Request", "Error parsing request body");
+			goto fin;
+		}
+	}
+	if (ast_ari_channels_redirect_parse_body(body, &args)) {
+		ast_ari_response_alloc_failed(response);
+		goto fin;
+	}
+	ast_ari_channels_redirect(headers, &args, response);
+#if defined(AST_DEVMODE)
+	code = response->response_code;
+
+	switch (code) {
+	case 0: /* Implementation is still a stub, or the code wasn't set */
+		is_valid = response->message == NULL;
+		break;
+	case 500: /* Internal Server Error */
+	case 501: /* Not Implemented */
+	case 400: /* Endpoint parameter not provided */
+	case 404: /* Channel or endpoint not found */
+	case 409: /* Channel not in a Stasis application */
+	case 422: /* Endpoint is not the same type as the channel */
+		is_valid = 1;
+		break;
+	default:
+		if (200 <= code && code <= 299) {
+			is_valid = ast_ari_validate_void(
+				response->message);
+		} else {
+			ast_log(LOG_ERROR, "Invalid error response %d for /channels/{channelId}/redirect\n", code);
+			is_valid = 0;
+		}
+	}
+
+	if (!is_valid) {
+		ast_log(LOG_ERROR, "Response validation failed for /channels/{channelId}/redirect\n");
+		ast_ari_response_error(response, 500,
+			"Internal Server Error", "Response validation failed");
+	}
+#endif /* AST_DEVMODE */
+
+fin: __attribute__((unused))
+	return;
+}
 /*!
  * \brief Parameter parsing callback for /channels/{channelId}/answer.
  * \param get_params GET parameters in the HTTP request.
@@ -2038,7 +2174,7 @@ static void ast_ari_channels_get_channel_var_cb(
 	case 500: /* Internal Server Error */
 	case 501: /* Not Implemented */
 	case 400: /* Missing variable parameter. */
-	case 404: /* Channel not found */
+	case 404: /* Channel or variable not found */
 	case 409: /* Channel not in a Stasis application */
 		is_valid = 1;
 		break;
@@ -2427,6 +2563,15 @@ static struct stasis_rest_handlers channels_channelId_continue = {
 	.children = {  }
 };
 /*! \brief REST handler for /api-docs/channels.{format} */
+static struct stasis_rest_handlers channels_channelId_redirect = {
+	.path_segment = "redirect",
+	.callbacks = {
+		[AST_HTTP_POST] = ast_ari_channels_redirect_cb,
+	},
+	.num_children = 0,
+	.children = {  }
+};
+/*! \brief REST handler for /api-docs/channels.{format} */
 static struct stasis_rest_handlers channels_channelId_answer = {
 	.path_segment = "answer",
 	.callbacks = {
@@ -2560,8 +2705,8 @@ static struct stasis_rest_handlers channels_channelId = {
 		[AST_HTTP_POST] = ast_ari_channels_originate_with_id_cb,
 		[AST_HTTP_DELETE] = ast_ari_channels_hangup_cb,
 	},
-	.num_children = 12,
-	.children = { &channels_channelId_continue,&channels_channelId_answer,&channels_channelId_ring,&channels_channelId_dtmf,&channels_channelId_mute,&channels_channelId_hold,&channels_channelId_moh,&channels_channelId_silence,&channels_channelId_play,&channels_channelId_record,&channels_channelId_variable,&channels_channelId_snoop, }
+	.num_children = 13,
+	.children = { &channels_channelId_continue,&channels_channelId_redirect,&channels_channelId_answer,&channels_channelId_ring,&channels_channelId_dtmf,&channels_channelId_mute,&channels_channelId_hold,&channels_channelId_moh,&channels_channelId_silence,&channels_channelId_play,&channels_channelId_record,&channels_channelId_variable,&channels_channelId_snoop, }
 };
 /*! \brief REST handler for /api-docs/channels.{format} */
 static struct stasis_rest_handlers channels = {
diff --git a/res/res_ari_device_states.c b/res/res_ari_device_states.c
index 1447525..f15e3da 100644
--- a/res/res_ari_device_states.c
+++ b/res/res_ari_device_states.c
@@ -33,13 +33,14 @@
 
 /*** MODULEINFO
 	<depend type="module">res_ari</depend>
+	<depend type="module">res_ari_model</depend>
 	<depend type="module">res_stasis</depend>
 	<support_level>core</support_level>
  ***/
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/app.h"
 #include "asterisk/module.h"
diff --git a/res/res_ari_endpoints.c b/res/res_ari_endpoints.c
index ed3c14d..4d54ff3 100644
--- a/res/res_ari_endpoints.c
+++ b/res/res_ari_endpoints.c
@@ -33,13 +33,14 @@
 
 /*** MODULEINFO
 	<depend type="module">res_ari</depend>
+	<depend type="module">res_ari_model</depend>
 	<depend type="module">res_stasis</depend>
 	<support_level>core</support_level>
  ***/
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420098 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/app.h"
 #include "asterisk/module.h"
@@ -170,7 +171,7 @@ static void ast_ari_endpoints_send_message_cb(
 			goto fin;
 		}
 	}
-	args.variables = ast_json_ref(body);
+	args.variables = body;
 	ast_ari_endpoints_send_message(headers, &args, response);
 #if defined(AST_DEVMODE)
 	code = response->response_code;
@@ -181,6 +182,7 @@ static void ast_ari_endpoints_send_message_cb(
 		break;
 	case 500: /* Internal Server Error */
 	case 501: /* Not Implemented */
+	case 400: /* Invalid parameters for sending a message. */
 	case 404: /* Endpoint not found */
 		is_valid = 1;
 		break;
@@ -396,7 +398,7 @@ static void ast_ari_endpoints_send_message_to_endpoint_cb(
 			goto fin;
 		}
 	}
-	args.variables = ast_json_ref(body);
+	args.variables = body;
 	ast_ari_endpoints_send_message_to_endpoint(headers, &args, response);
 #if defined(AST_DEVMODE)
 	code = response->response_code;
diff --git a/res/res_ari_events.c b/res/res_ari_events.c
index c1b3434..65bd38d 100644
--- a/res/res_ari_events.c
+++ b/res/res_ari_events.c
@@ -33,13 +33,14 @@
 
 /*** MODULEINFO
 	<depend type="module">res_ari</depend>
+	<depend type="module">res_ari_model</depend>
 	<depend type="module">res_stasis</depend>
 	<support_level>core</support_level>
  ***/
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/app.h"
 #include "asterisk/module.h"
@@ -52,7 +53,95 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
 
 #define MAX_VALS 128
 
-static void ast_ari_events_event_websocket_ws_cb(struct ast_websocket *ws_session,
+static int ast_ari_events_event_websocket_ws_attempted_cb(struct ast_tcptls_session_instance *ser, struct ast_variable *get_params, struct ast_variable *headers)
+{
+	struct ast_ari_events_event_websocket_args args = {};
+	int res = 0;
+	RAII_VAR(struct ast_ari_response *, response, NULL, ast_free);
+	struct ast_variable *i;
+
+	response = ast_calloc(1, sizeof(*response));
+	if (!response) {
+		ast_log(LOG_ERROR, "Failed to create response.\n");
+		goto fin;
+	}
+
+	for (i = get_params; i; i = i->next) {
+		if (strcmp(i->name, "app") == 0) {
+			/* Parse comma separated list */
+			char *vals[MAX_VALS];
+			size_t j;
+
+			args.app_parse = ast_strdup(i->value);
+			if (!args.app_parse) {
+				ast_ari_response_alloc_failed(response);
+				goto fin;
+			}
+
+			if (strlen(args.app_parse) == 0) {
+				/* ast_app_separate_args can't handle "" */
+				args.app_count = 1;
+				vals[0] = args.app_parse;
+			} else {
+				args.app_count = ast_app_separate_args(
+					args.app_parse, ',', vals,
+					ARRAY_LEN(vals));
+			}
+
+			if (args.app_count == 0) {
+				ast_ari_response_alloc_failed(response);
+				goto fin;
+			}
+
+			if (args.app_count >= MAX_VALS) {
+				ast_ari_response_error(response, 400,
+					"Bad Request",
+					"Too many values for app");
+				goto fin;
+			}
+
+			args.app = ast_malloc(sizeof(*args.app) * args.app_count);
+			if (!args.app) {
+				ast_ari_response_alloc_failed(response);
+				goto fin;
+			}
+
+			for (j = 0; j < args.app_count; ++j) {
+				args.app[j] = (vals[j]);
+			}
+		} else
+		if (strcmp(i->name, "subscribeAll") == 0) {
+			args.subscribe_all = ast_true(i->value);
+		} else
+		{}
+	}
+
+	res = ast_ari_websocket_events_event_websocket_attempted(ser, headers, &args);
+
+fin: __attribute__((unused))
+	if (!response) {
+		ast_http_error(ser, 500, "Server Error", "Memory allocation error");
+		res = -1;
+	} else if (response->response_code != 0) {
+		/* Param parsing failure */
+		RAII_VAR(char *, msg, NULL, ast_json_free);
+		if (response->message) {
+			msg = ast_json_dump_string(response->message);
+		} else {
+			ast_log(LOG_ERROR, "Missing response message\n");
+		}
+
+		if (msg) {
+			ast_http_error(ser, response->response_code, response->response_text, msg);
+		}
+		res = -1;
+	}
+	ast_free(args.app_parse);
+	ast_free(args.app);
+	return res;
+}
+
+static void ast_ari_events_event_websocket_ws_established_cb(struct ast_websocket *ws_session,
 	struct ast_variable *get_params, struct ast_variable *headers)
 {
 	struct ast_ari_events_event_websocket_args args = {};
@@ -122,19 +211,17 @@ static void ast_ari_events_event_websocket_ws_cb(struct ast_websocket *ws_sessio
 				args.app[j] = (vals[j]);
 			}
 		} else
+		if (strcmp(i->name, "subscribeAll") == 0) {
+			args.subscribe_all = ast_true(i->value);
+		} else
 		{}
 	}
 
-	ast_ari_websocket_events_event_websocket(session, headers, &args);
+	ast_ari_websocket_events_event_websocket_established(session, headers, &args);
 
 fin: __attribute__((unused))
 	if (response && response->response_code != 0) {
 		/* Param parsing failure */
-		/* TODO - ideally, this would return the error code to the
-		 * HTTP client; but we've already done the WebSocket
-		 * negotiation. Param parsing should happen earlier, but we
-		 * need a way to pass it through the WebSocket code to the
-		 * callback */
 		RAII_VAR(char *, msg, NULL, ast_json_free);
 		if (response->message) {
 			msg = ast_json_dump_string(response->message);
@@ -281,7 +368,7 @@ static void ast_ari_events_user_event_cb(
 			goto fin;
 		}
 	}
-	args.variables = ast_json_ref(body);
+	args.variables = body;
 	ast_ari_events_user_event(headers, &args, response);
 #if defined(AST_DEVMODE)
 	code = response->response_code;
@@ -350,12 +437,22 @@ static struct stasis_rest_handlers events = {
 static int load_module(void)
 {
 	int res = 0;
+	struct ast_websocket_protocol *protocol;
+
 	events.ws_server = ast_websocket_server_create();
 	if (!events.ws_server) {
 		return AST_MODULE_LOAD_FAILURE;
 	}
-	res |= ast_websocket_server_add_protocol(events.ws_server,
-		"ari", ast_ari_events_event_websocket_ws_cb);
+
+	protocol = ast_websocket_sub_protocol_alloc("ari");
+	if (!protocol) {
+		ao2_ref(events.ws_server, -1);
+		events.ws_server = NULL;
+		return AST_MODULE_LOAD_FAILURE;
+	}
+	protocol->session_attempted = ast_ari_events_event_websocket_ws_attempted_cb;
+	protocol->session_established = ast_ari_events_event_websocket_ws_established_cb;
+	res |= ast_websocket_server_add_protocol2(events.ws_server, protocol);
 	stasis_app_ref();
 	res |= ast_ari_add_handler(&events);
 	return res;
diff --git a/res/res_ari_mailboxes.c b/res/res_ari_mailboxes.c
index 590088d..2b3404f 100644
--- a/res/res_ari_mailboxes.c
+++ b/res/res_ari_mailboxes.c
@@ -33,13 +33,14 @@
 
 /*** MODULEINFO
 	<depend type="module">res_ari</depend>
+	<depend type="module">res_ari_model</depend>
 	<depend type="module">res_stasis</depend>
 	<support_level>core</support_level>
  ***/
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/app.h"
 #include "asterisk/module.h"
diff --git a/res/res_ari_model.c b/res/res_ari_model.c
index ff4fe5d..96cab80 100644
--- a/res/res_ari_model.c
+++ b/res/res_ari_model.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "ari/ari_model_validators.h"
 #include "asterisk/logger.h"
diff --git a/res/res_ari_playbacks.c b/res/res_ari_playbacks.c
index 94662f2..de38ad8 100644
--- a/res/res_ari_playbacks.c
+++ b/res/res_ari_playbacks.c
@@ -33,13 +33,14 @@
 
 /*** MODULEINFO
 	<depend type="module">res_ari</depend>
+	<depend type="module">res_ari_model</depend>
 	<depend type="module">res_stasis</depend>
 	<support_level>core</support_level>
  ***/
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/app.h"
 #include "asterisk/module.h"
diff --git a/res/res_ari_recordings.c b/res/res_ari_recordings.c
index a94d31a..dede53c 100644
--- a/res/res_ari_recordings.c
+++ b/res/res_ari_recordings.c
@@ -33,13 +33,14 @@
 
 /*** MODULEINFO
 	<depend type="module">res_ari</depend>
+	<depend type="module">res_ari_model</depend>
 	<depend type="module">res_stasis</depend>
 	<support_level>core</support_level>
  ***/
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/app.h"
 #include "asterisk/module.h"
diff --git a/res/res_ari_sounds.c b/res/res_ari_sounds.c
index e27286a..c53330d 100644
--- a/res/res_ari_sounds.c
+++ b/res/res_ari_sounds.c
@@ -33,13 +33,14 @@
 
 /*** MODULEINFO
 	<depend type="module">res_ari</depend>
+	<depend type="module">res_ari_model</depend>
 	<depend type="module">res_stasis</depend>
 	<support_level>core</support_level>
  ***/
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/app.h"
 #include "asterisk/module.h"
diff --git a/res/res_calendar.c b/res/res_calendar.c
index 834b655..5b911ca 100644
--- a/res/res_calendar.c
+++ b/res/res_calendar.c
@@ -38,7 +38,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428246 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/_private.h"
 #include "asterisk/channel.h"
@@ -1106,8 +1106,8 @@ static struct ast_custom_function calendar_busy_function = {
 static int add_event_to_list(struct eventlist *events, struct ast_calendar_event *event, time_t start, time_t end)
 {
 	struct evententry *entry, *iter;
-	int event_startdiff = abs(start - event->start);
-	int event_enddiff = abs(end - event->end);
+	long event_startdiff = labs(start - event->start);
+	long event_enddiff = labs(end - event->end);
 	int i = 0;
 
 	if (!(entry = ast_calloc(1, sizeof(*entry)))) {
@@ -1120,16 +1120,16 @@ static int add_event_to_list(struct eventlist *events, struct ast_calendar_event
 
 	if (start == end) {
 		AST_LIST_TRAVERSE_SAFE_BEGIN(events, iter, list) {
-			int startdiff = abs(iter->event->start - start);
+			long startdiff = labs(iter->event->start - start);
 
-			ast_debug(10, "Comparing %s with startdiff %d to %s with startdiff %d\n", event->summary, event_startdiff, iter->event->summary, startdiff);
+			ast_debug(10, "Comparing %s with startdiff %ld to %s with startdiff %ld\n", event->summary, event_startdiff, iter->event->summary, startdiff);
 			++i;
 			if (startdiff > event_startdiff) {
 				AST_LIST_INSERT_BEFORE_CURRENT(entry, list);
 				return i;
 			}
 			if (startdiff == event_startdiff) {
-				int enddiff = abs(iter->event->end - end);
+				long enddiff = labs(iter->event->end - end);
 
 				if (enddiff > event_enddiff) {
 					AST_LIST_INSERT_BEFORE_CURRENT(entry, list);
diff --git a/res/res_calendar_caldav.c b/res/res_calendar_caldav.c
index 59fb03c..a8eac7c 100644
--- a/res/res_calendar_caldav.c
+++ b/res/res_calendar_caldav.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <libical/ical.h>
 #include <ne_session.h>
diff --git a/res/res_calendar_ews.c b/res/res_calendar_ews.c
index 3d7f862..c45ac0b 100644
--- a/res/res_calendar_ews.c
+++ b/res/res_calendar_ews.c
@@ -27,7 +27,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 425289 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <ne_request.h>
 #include <ne_session.h>
diff --git a/res/res_calendar_exchange.c b/res/res_calendar_exchange.c
index 9777efc..e8c80cd 100644
--- a/res/res_calendar_exchange.c
+++ b/res/res_calendar_exchange.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <libical/ical.h>
 #include <ne_session.h>
diff --git a/res/res_calendar_icalendar.c b/res/res_calendar_icalendar.c
index 823fc83..8ac9051 100644
--- a/res/res_calendar_icalendar.c
+++ b/res/res_calendar_icalendar.c
@@ -28,7 +28,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <libical/ical.h>
 #include <ne_session.h>
diff --git a/res/res_chan_stats.c b/res/res_chan_stats.c
index 50a5fd8..8d446b3 100644
--- a/res/res_chan_stats.c
+++ b/res/res_chan_stats.c
@@ -34,7 +34,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/stasis_channels.h"
@@ -111,7 +111,7 @@ static void updates(void *data, struct stasis_subscription *sub,
 
 	if (!update->old_snapshot && update->new_snapshot) {
 		/* Initial cache entry; count a channel creation */
-		ast_statsd_log("channels.count", AST_STATSD_COUNTER, 1);
+		ast_statsd_log_string("channels.count", AST_STATSD_GAUGE, "+1", 1.0);
 	} else if (update->old_snapshot && !update->new_snapshot) {
 		/* Cache entry removed. Compute the age of the channel and post
 		 * that, as well as decrementing the channel count.
@@ -125,7 +125,7 @@ static void updates(void *data, struct stasis_subscription *sub,
 		ast_statsd_log("channels.calltime", AST_STATSD_TIMER, age);
 
 		/* And decrement the channel count */
-		ast_statsd_log("channels.count", AST_STATSD_COUNTER, -1);
+		ast_statsd_log_string("channels.count", AST_STATSD_GAUGE, "-1", 1.0);
 	}
 }
 
diff --git a/res/res_clialiases.c b/res/res_clialiases.c
index 4f67158..633ed89 100644
--- a/res/res_clialiases.c
+++ b/res/res_clialiases.c
@@ -41,7 +41,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/config.h"
diff --git a/res/res_clioriginate.c b/res/res_clioriginate.c
index 7e17fd4..3903b6c 100644
--- a/res/res_clioriginate.c
+++ b/res/res_clioriginate.c
@@ -30,7 +30,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419044 $");
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
 
 #include "asterisk/channel.h"
 #include "asterisk/pbx.h"
diff --git a/res/res_config_curl.c b/res/res_config_curl.c
index 44d3f1a..a518029 100644
--- a/res/res_config_curl.c
+++ b/res/res_config_curl.c
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <curl/curl.h>
 
diff --git a/res/res_config_ldap.c b/res/res_config_ldap.c
index d2b8b4a..fd21aab 100644
--- a/res/res_config_ldap.c
+++ b/res/res_config_ldap.c
@@ -53,7 +53,7 @@
 #include <stdio.h>
 #include <ldap.h>
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/channel.h"
 #include "asterisk/logger.h"
diff --git a/res/res_config_odbc.c b/res/res_config_odbc.c
index fe89b55..2888d35 100644
--- a/res/res_config_odbc.c
+++ b/res/res_config_odbc.c
@@ -35,7 +35,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/file.h"
 #include "asterisk/channel.h"
@@ -198,11 +198,11 @@ static struct ast_variable *realtime_odbc(const char *database, const char *tabl
 
 	op = !strchr(field->name, ' ') ? " =" : "";
 	snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s ?%s", table, field->name, op,
-		strcasestr(field->name, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
+		strcasestr(field->name, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\\\'" : "");
 	while ((field = field->next)) {
 		op = !strchr(field->name, ' ') ? " =" : "";
 		snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s ?%s", field->name, op,
-			strcasestr(field->name, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
+			strcasestr(field->name, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\\\'" : "");
 	}
 
 	if (ast_string_field_init(&cps, 256)) {
@@ -360,11 +360,11 @@ static struct ast_config *realtime_multi_odbc(const char *database, const char *
 
 	op = !strchr(field->name, ' ') ? " =" : "";
 	snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s ?%s", table, field->name, op,
-		strcasestr(field->name, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
+		strcasestr(field->name, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\\\'" : "");
 	while ((field = field->next)) {
 		op = !strchr(field->name, ' ') ? " =" : "";
 		snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s ?%s", field->name, op,
-			strcasestr(field->name, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
+			strcasestr(field->name, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\\\'" : "");
 	}
 
 	snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " ORDER BY %s", initfield);
diff --git a/res/res_config_pgsql.c b/res/res_config_pgsql.c
index c836749..52c8ede 100644
--- a/res/res_config_pgsql.c
+++ b/res/res_config_pgsql.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <libpq-fe.h>			/* PostgreSQL */
 
@@ -336,7 +336,9 @@ static struct tables *find_table(const char *database, const char *orig_tablenam
 		ast_str_set(&sql, 0, "SELECT a.attname, t.typname, a.attlen, a.attnotnull, d.adsrc, a.atttypmod FROM pg_class c, pg_type t, pg_attribute a LEFT OUTER JOIN pg_attrdef d ON a.atthasdef AND d.adrelid = a.attrelid AND d.adnum = a.attnum WHERE c.oid = a.attrelid AND a.atttypid = t.oid AND (a.attnum > 0) AND c.relname = '%s' ORDER BY c.relname, attnum", orig_tablename);
 	}
 
+	ast_mutex_lock(&pgsql_lock);
 	exec_result = pgsql_exec(database, orig_tablename, ast_str_buffer(sql), &result);
+	ast_mutex_unlock(&pgsql_lock);
 	ast_debug(1, "Query of table structure complete.  Now retrieving results.\n");
 	if (exec_result != 0) {
 		ast_log(LOG_ERROR, "Failed to query database columns for table %s\n", orig_tablename);
@@ -1354,6 +1356,9 @@ static int unload_module(void)
 	ast_cli_unregister_multiple(cli_realtime, ARRAY_LEN(cli_realtime));
 	ast_config_engine_deregister(&pgsql_engine);
 
+	/* Unlock so something else can destroy the lock. */
+	ast_mutex_unlock(&pgsql_lock);
+
 	/* Destroy cached table info */
 	AST_LIST_LOCK(&psql_tables);
 	while ((table = AST_LIST_REMOVE_HEAD(&psql_tables, list))) {
@@ -1361,9 +1366,6 @@ static int unload_module(void)
 	}
 	AST_LIST_UNLOCK(&psql_tables);
 
-	/* Unlock so something else can destroy the lock. */
-	ast_mutex_unlock(&pgsql_lock);
-
 	return 0;
 }
 
diff --git a/res/res_config_sqlite.c b/res/res_config_sqlite.c
index 324a9a2..b97ef7a 100644
--- a/res/res_config_sqlite.c
+++ b/res/res_config_sqlite.c
@@ -86,7 +86,7 @@
  ***/
 
 #include "asterisk.h"
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sqlite.h>
 
@@ -781,8 +781,16 @@ static int cdr_handler(struct ast_cdr *cdr)
 	struct ast_str *sql1 = ast_str_create(160), *sql2 = ast_str_create(16);
 	int first = 1;
 
+	if (!sql1 || !sql2) {
+		ast_free(sql1);
+		ast_free(sql2);
+		return -1;
+	}
+
 	if (!tbl) {
 		ast_log(LOG_WARNING, "No such table: %s\n", cdr_table);
+		ast_free(sql1);
+		ast_free(sql2);
 		return -1;
 	}
 
diff --git a/res/res_config_sqlite3.c b/res/res_config_sqlite3.c
index f3e0435..04b8e37 100644
--- a/res/res_config_sqlite3.c
+++ b/res/res_config_sqlite3.c
@@ -45,7 +45,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sqlite3.h>
 
diff --git a/res/res_convert.c b/res/res_convert.c
index 83342ce..c2966a8 100644
--- a/res/res_convert.c
+++ b/res/res_convert.c
@@ -32,7 +32,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 328259 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/channel.h"
 #include "asterisk/module.h"
diff --git a/res/res_corosync.c b/res/res_corosync.c
index 0b6a06f..72da3f1 100644
--- a/res/res_corosync.c
+++ b/res/res_corosync.c
@@ -32,7 +32,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420124 $");
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
 
 #include <corosync/cpg.h>
 #include <corosync/cfg.h>
@@ -863,7 +863,6 @@ static void cleanup_module(void)
 static int load_module(void)
 {
 	cs_error_t cs_err;
-	enum ast_module_load_result res = AST_MODULE_LOAD_FAILURE;
 	struct cpg_name name;
 
 	corosync_aggregate_topic = stasis_topic_create("corosync_aggregate_topic");
@@ -883,6 +882,11 @@ static int load_module(void)
 		goto failed;
 	}
 
+	if (load_config(0)) {
+		/* simply not configured is not a fatal error */
+		goto failed;
+	}
+
 	if ((cs_err = corosync_cfg_initialize(&cfg_handle, &cfg_callbacks)) != CS_OK) {
 		ast_log(LOG_ERROR, "Failed to initialize cfg: (%d)\n", (int) cs_err);
 		goto failed;
@@ -913,12 +917,6 @@ static int load_module(void)
 		goto failed;
 	}
 
-	if (load_config(0)) {
-		/* simply not configured is not a fatal error */
-		res = AST_MODULE_LOAD_DECLINE;
-		goto failed;
-	}
-
 	ast_cli_register_multiple(corosync_cli, ARRAY_LEN(corosync_cli));
 
 	return AST_MODULE_LOAD_SUCCESS;
@@ -926,7 +924,7 @@ static int load_module(void)
 failed:
 	cleanup_module();
 
-	return res;
+	return AST_MODULE_LOAD_DECLINE;
 }
 
 static int unload_module(void)
diff --git a/res/res_crypto.c b/res/res_crypto.c
index 71634d0..78b8df2 100644
--- a/res/res_crypto.c
+++ b/res/res_crypto.c
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/paths.h"	/* use ast_config_AST_KEY_DIR */
 #include <openssl/ssl.h>
@@ -532,7 +532,7 @@ static void md52sum(char *sum, unsigned char *md5)
 {
 	int x;
 	for (x = 0; x < 16; x++) {
-		sum += sprintf(sum, "%02x", (unsigned)*(md5++));
+		sum += sprintf(sum, "%02hhx", *(md5++));
 	}
 }
 
diff --git a/res/res_curl.c b/res/res_curl.c
index da35c98..4906dde 100644
--- a/res/res_curl.c
+++ b/res/res_curl.c
@@ -42,7 +42,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <curl/curl.h>
 
diff --git a/res/res_endpoint_stats.c b/res/res_endpoint_stats.c
new file mode 100644
index 0000000..6b84794
--- /dev/null
+++ b/res/res_endpoint_stats.c
@@ -0,0 +1,157 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2015, Digium, Inc.
+ *
+ * Matthew Jordan <mjordan at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \brief Statsd Endpoint stats.
+ *
+ * This module subscribes to Stasis endpoints and send statistics
+ * based on their state.
+ *
+ * \author Matthew Jordan <mjordan at digium.com>
+ * \since 13.7.0
+ */
+
+/*** MODULEINFO
+	<depend>res_statsd</depend>
+	<defaultenabled>no</defaultenabled>
+	<support_level>extended</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/stasis_endpoints.h"
+#include "asterisk/stasis_message_router.h"
+#include "asterisk/statsd.h"
+
+/*! Stasis message router */
+static struct stasis_message_router *router;
+
+static void update_endpoint_state(struct ast_endpoint_snapshot *snapshot, const char *delta)
+{
+	switch (snapshot->state) {
+	case AST_ENDPOINT_UNKNOWN:
+		ast_statsd_log_string("endpoints.state.unknown", AST_STATSD_GAUGE, delta, 1.0);
+		break;
+	case AST_ENDPOINT_OFFLINE:
+		ast_statsd_log_string("endpoints.state.offline", AST_STATSD_GAUGE, delta, 1.0);
+		break;
+	case AST_ENDPOINT_ONLINE:
+		ast_statsd_log_string("endpoints.state.online", AST_STATSD_GAUGE, delta, 1.0);
+		break;
+	}
+}
+
+static void handle_endpoint_update(struct ast_endpoint_snapshot *old_snapshot, struct ast_endpoint_snapshot *new_snapshot)
+{
+	if (!old_snapshot && new_snapshot) {
+		ast_statsd_log_string("endpoints.count", AST_STATSD_GAUGE, "+1", 1.0);
+		update_endpoint_state(new_snapshot, "+1");
+	} else if (old_snapshot && !new_snapshot) {
+		ast_statsd_log_string("endpoints.count", AST_STATSD_GAUGE, "-1", 1.0);
+		update_endpoint_state(old_snapshot, "-1");
+	} else {
+		if (old_snapshot->state != new_snapshot->state) {
+			update_endpoint_state(old_snapshot, "-1");
+			update_endpoint_state(new_snapshot, "+1");
+		}
+		ast_statsd_log_full_va("endpoints.%s.%s.channels", AST_STATSD_GAUGE, new_snapshot->num_channels, 1.0,
+			new_snapshot->tech, new_snapshot->resource);
+	}
+}
+
+static void cache_update_cb(void *data, struct stasis_subscription *sub,
+	struct stasis_message *message)
+{
+	struct stasis_cache_update *update = stasis_message_data(message);
+	struct ast_endpoint_snapshot *old_snapshot;
+	struct ast_endpoint_snapshot *new_snapshot;
+
+	if (ast_endpoint_snapshot_type() != update->type) {
+		return;
+	}
+
+	old_snapshot = stasis_message_data(update->old_snapshot);
+	new_snapshot = stasis_message_data(update->new_snapshot);
+
+	handle_endpoint_update(old_snapshot, new_snapshot);
+}
+
+static int dump_cache_load(void *obj, void *arg, int flags)
+{
+	struct stasis_message *msg = obj;
+	struct ast_endpoint_snapshot *snapshot = stasis_message_data(msg);
+
+	handle_endpoint_update(NULL, snapshot);
+
+	return 0;
+}
+
+static int dump_cache_unload(void *obj, void *arg, int flags)
+{
+	struct stasis_message *msg = obj;
+	struct ast_endpoint_snapshot *snapshot = stasis_message_data(msg);
+
+	handle_endpoint_update(snapshot, NULL);
+
+	return 0;
+}
+
+static int load_module(void)
+{
+	struct ao2_container *endpoints;
+
+	router = stasis_message_router_create(ast_endpoint_topic_all_cached());
+	if (!router) {
+		return AST_MODULE_LOAD_FAILURE;
+	}
+	stasis_message_router_add(router, stasis_cache_update_type(), cache_update_cb, NULL);
+
+	endpoints = stasis_cache_dump(ast_endpoint_cache(), ast_endpoint_snapshot_type());
+	if (endpoints) {
+		ao2_callback(endpoints, OBJ_MULTIPLE | OBJ_NODATA | OBJ_NOLOCK, dump_cache_load, NULL);
+		ao2_ref(endpoints, -1);
+	}
+
+	return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+	struct ao2_container *endpoints;
+
+	endpoints = stasis_cache_dump(ast_endpoint_cache(), ast_endpoint_snapshot_type());
+	if (endpoints) {
+		ao2_callback(endpoints, OBJ_MULTIPLE | OBJ_NODATA | OBJ_NOLOCK, dump_cache_unload, NULL);
+		ao2_ref(endpoints, -1);
+	}
+
+	stasis_message_router_unsubscribe_and_join(router);
+	router = NULL;
+
+	return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Endpoint statistics",
+	.support_level = AST_MODULE_SUPPORT_EXTENDED,
+	.load = load_module,
+	.unload = unload_module,
+	.nonoptreq = "res_statsd"
+	);
diff --git a/res/res_fax.c b/res/res_fax.c
index 46fe99e..ef0e276 100644
--- a/res/res_fax.c
+++ b/res/res_fax.c
@@ -65,7 +65,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 426529 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/io.h"
 #include "asterisk/file.h"
@@ -225,12 +225,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 426529 $")
 					<enum name="statusstr">
 						<para>R/O Verbose Result Status of the FAX transmission.</para>
 					</enum>
+					<enum name="t38timeout">
+						<para>R/W The timeout used for T.38 negotiation.</para>
+					</enum>
 				</enumlist>
 			</parameter>
 		</syntax>
 		<description>
 			<para>FAXOPT can be used to override the settings for a FAX session listed in <filename>res_fax.conf</filename>,
-		   	it can also be used to retreive information about a FAX session that has finished eg. pages/status.</para>
+		   	it can also be used to retrieve information about a FAX session that has finished eg. pages/status.</para>
 		</description>
 		<see-also>
 			<ref type="application">ReceiveFax</ref>
@@ -520,7 +523,8 @@ static AST_RWLIST_HEAD_STATIC(faxmodules, fax_module);
 #define RES_FAX_MINRATE 4800
 #define RES_FAX_MAXRATE 14400
 #define RES_FAX_STATUSEVENTS 0
-#define RES_FAX_MODEM (AST_FAX_MODEM_V17 | AST_FAX_MODEM_V27 | AST_FAX_MODEM_V29)
+#define RES_FAX_MODEM (AST_FAX_MODEM_V17 | AST_FAX_MODEM_V27TER | AST_FAX_MODEM_V29)
+#define RES_FAX_T38TIMEOUT 5000
 
 struct fax_options {
 	enum ast_fax_modems modems;
@@ -528,6 +532,7 @@ struct fax_options {
 	uint32_t ecm:1;
 	unsigned int minrate;
 	unsigned int maxrate;
+	unsigned int t38timeout;
 };
 
 static struct fax_options general_options;
@@ -538,6 +543,7 @@ static const struct fax_options default_options = {
 	.statusevents = RES_FAX_STATUSEVENTS,
 	.modems = RES_FAX_MODEM,
 	.ecm = AST_FAX_OPTFLAG_TRUE,
+	.t38timeout = RES_FAX_T38TIMEOUT,
 };
 
 AST_RWLOCK_DEFINE_STATIC(options_lock);
@@ -713,6 +719,7 @@ static struct ast_fax_session_details *session_details_new(void)
 	d->modems = options.modems;
 	d->minrate = options.minrate;
 	d->maxrate = options.maxrate;
+	d->t38timeout = options.t38timeout;
 	d->gateway_id = -1;
 	d->faxdetect_id = -1;
 	d->gateway_timeout = 0;
@@ -821,7 +828,7 @@ static int update_modem_bits(enum ast_fax_modems *bits, const char *value)
 		if (!strcasecmp(m[j], "v17")) {
 			*bits |= AST_FAX_MODEM_V17;
 		} else if (!strcasecmp(m[j], "v27")) {
-			*bits |= AST_FAX_MODEM_V27;
+			*bits |= AST_FAX_MODEM_V27TER;
 		} else if (!strcasecmp(m[j], "v29")) {
 			*bits |= AST_FAX_MODEM_V29;
 		} else if (!strcasecmp(m[j], "v34")) {
@@ -832,6 +839,7 @@ static int update_modem_bits(enum ast_fax_modems *bits, const char *value)
 	}
 	return 0;
 }
+
 static char *ast_fax_caps_to_str(enum ast_fax_capabilities caps, char *buf, size_t bufsize)
 {
 	char *out = buf;
@@ -899,7 +907,7 @@ static int ast_fax_modem_to_str(enum ast_fax_modems bits, char *tbuf, size_t buf
 		strcat(tbuf, "V17");
 		count++;
 	}
-	if (bits & AST_FAX_MODEM_V27) {
+	if (bits & AST_FAX_MODEM_V27TER) {
 		if (count) {
 			strcat(tbuf, ",");
 		}
@@ -928,22 +936,14 @@ static int check_modem_rate(enum ast_fax_modems modems, unsigned int rate)
 {
 	switch (rate) {
 	case 2400:
-		if (!(modems & (AST_FAX_MODEM_V34))) {
-			return 1;
-		}
-		break;
 	case 4800:
-		if (!(modems & (AST_FAX_MODEM_V27 | AST_FAX_MODEM_V34))) {
+		if (!(modems & (AST_FAX_MODEM_V27TER | AST_FAX_MODEM_V34))) {
 			return 1;
 		}
 		break;
 	case 7200:
-		if (!(modems & (AST_FAX_MODEM_V17 | AST_FAX_MODEM_V29 | AST_FAX_MODEM_V34))) {
-			return 1;
-		}
-		break;
 	case 9600:
-		if (!(modems & (AST_FAX_MODEM_V17 | AST_FAX_MODEM_V27 | AST_FAX_MODEM_V29 | AST_FAX_MODEM_V34))) {
+		if (!(modems & (AST_FAX_MODEM_V17 | AST_FAX_MODEM_V29 | AST_FAX_MODEM_V34))) {
 			return 1;
 		}
 		break;
@@ -1349,7 +1349,8 @@ static struct ast_json *generate_filenames_json(struct ast_fax_session_details *
 	return json_array;
 }
 
-/* \brief Generate a string of filenames using the given prefix and separator.
+/*!
+ * \brief Generate a string of filenames using the given prefix and separator.
  * \param details the fax session details
  * \param prefix the prefix to each filename
  * \param separator the separator between filenames
@@ -1617,21 +1618,15 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det
 		orig_write_format = ao2_bump(ast_channel_writeformat(chan));
 		if (ast_set_write_format(chan, ast_format_slin) < 0) {
 			ast_log(LOG_ERROR, "channel '%s' failed to set write format to signed linear'.\n", ast_channel_name(chan));
- 			ao2_lock(faxregistry.container);
- 			ao2_unlink(faxregistry.container, fax);
- 			ao2_unlock(faxregistry.container);
- 			ao2_ref(fax, -1);
-			ast_channel_unlock(chan);
+			ao2_unlink(faxregistry.container, fax);
+			ao2_ref(fax, -1);
 			return -1;
 		}
 		orig_read_format = ao2_bump(ast_channel_readformat(chan));
 		if (ast_set_read_format(chan, ast_format_slin) < 0) {
 			ast_log(LOG_ERROR, "channel '%s' failed to set read format to signed linear.\n", ast_channel_name(chan));
- 			ao2_lock(faxregistry.container);
- 			ao2_unlink(faxregistry.container, fax);
- 			ao2_unlock(faxregistry.container);
- 			ao2_ref(fax, -1);
-			ast_channel_unlock(chan);
+			ao2_unlink(faxregistry.container, fax);
+			ao2_ref(fax, -1);
 			return -1;
 		}
 		if (fax->smoother) {
@@ -1716,7 +1711,10 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det
 					break;
 				}
 				if (t38negotiated && !was_t38) {
-					fax->tech->switch_to_t38(fax);
+					if (fax->tech->switch_to_t38(fax)) {
+						GENERIC_FAX_EXEC_ERROR(fax, chan, "UNKNOWN", "T.38 switch failed");
+						break;
+					}
 					details->caps &= ~AST_FAX_TECH_AUDIO;
 					expected_frametype = AST_FRAME_MODEM;
 					expected_framesubclass.integer = AST_MODEM_T38;
@@ -1806,9 +1804,7 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det
 	}
 
 	if (fax) {
-		ao2_lock(faxregistry.container);
 		ao2_unlink(faxregistry.container, fax);
-		ao2_unlock(faxregistry.container);
 		ao2_ref(fax, -1);
 	}
 
@@ -1904,8 +1900,8 @@ static int receivefax_t38_init(struct ast_channel *chan, struct ast_fax_session_
 	/* request T.38 */
 	ast_debug(1, "Negotiating T.38 for receive on %s\n", ast_channel_name(chan));
 
-	/* wait up to five seconds for negotiation to complete */
-	timeout_ms = 5000;
+	/* wait for negotiation to complete */
+	timeout_ms = details->t38timeout;
 
 	/* set parameters based on the session's parameters */
 	t38_parameters_fax_to_ast(&t38_parameters, &details->our_t38_parameters);
@@ -2032,14 +2028,14 @@ static int report_receive_fax_status(struct ast_channel *chan, const char *filen
 			fax_bitrate = ast_strdupa(fax_bitrate);
 		}
 
-		json_object = ast_json_pack("{s: s, s: s, s: s, s: s, s: s, s: s, s: O}",
+		json_object = ast_json_pack("{s: s, s: s, s: s, s: s, s: s, s: s, s: o}",
 				"type", "receive",
 				"remote_station_id", S_OR(remote_station_id, ""),
 				"local_station_id", S_OR(local_station_id, ""),
 				"fax_pages", S_OR(fax_pages, ""),
 				"fax_resolution", S_OR(fax_resolution, ""),
 				"fax_bitrate", S_OR(fax_bitrate, ""),
-				"filenames", json_array);
+				"filenames", ast_json_ref(json_array));
 		if (!json_object) {
 			return -1;
 		}
@@ -2794,18 +2790,14 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
 static void destroy_v21_sessions(struct fax_gateway *gateway)
 {
 	if (gateway->chan_v21_session) {
-		ao2_lock(faxregistry.container);
 		ao2_unlink(faxregistry.container, gateway->chan_v21_session);
-		ao2_unlock(faxregistry.container);
 
 		ao2_ref(gateway->chan_v21_session, -1);
 		gateway->chan_v21_session = NULL;
 	}
 
 	if (gateway->peer_v21_session) {
-		ao2_lock(faxregistry.container);
 		ao2_unlink(faxregistry.container, gateway->peer_v21_session);
-		ao2_unlock(faxregistry.container);
 
 		ao2_ref(gateway->peer_v21_session, -1);
 		gateway->peer_v21_session = NULL;
@@ -2823,9 +2815,7 @@ static void destroy_gateway(void *data)
 		fax_session_release(gateway->s, gateway->token);
 		gateway->token = NULL;
 
-		ao2_lock(faxregistry.container);
 		ao2_unlink(faxregistry.container, gateway->s);
-		ao2_unlock(faxregistry.container);
 
 		ao2_ref(gateway->s, -1);
 		gateway->s = NULL;
@@ -2890,6 +2880,7 @@ static struct fax_gateway *fax_gateway_new(struct ast_channel *chan, struct ast_
 static int fax_gateway_start(struct fax_gateway *gateway, struct ast_fax_session_details *details, struct ast_channel *chan)
 {
 	struct ast_fax_session *s;
+	int start_res;
 
 	/* create the FAX session */
 	if (!(s = fax_session_new(details, chan, gateway->s, gateway->token))) {
@@ -2910,7 +2901,10 @@ static int fax_gateway_start(struct fax_gateway *gateway, struct ast_fax_session
 	gateway->s = s;
 	gateway->token = NULL;
 
-	if (gateway->s->tech->start_session(gateway->s) < 0) {
+	ast_channel_unlock(chan);
+	start_res = gateway->s->tech->start_session(gateway->s);
+	ast_channel_lock(chan);
+	if (start_res < 0) {
 		ast_string_field_set(details, result, "FAILED");
 		ast_string_field_set(details, resultstr, "error starting gateway session");
 		ast_string_field_set(details, error, "INIT_ERROR");
@@ -3226,7 +3220,8 @@ static struct ast_frame *fax_gateway_detect_t38(struct fax_gateway *gateway, str
 
 /*! \brief Destroy the gateway data structure when the framehook is detached
  * \param data framehook data (gateway data)*/
-static void fax_gateway_framehook_destroy(void *data) {
+static void fax_gateway_framehook_destroy(void *data)
+{
 	struct fax_gateway *gateway = data;
 
 	if (gateway->s) {
@@ -3260,7 +3255,8 @@ static void fax_gateway_framehook_destroy(void *data) {
  *
  * \return processed frame or NULL when f is NULL or a null frame
  */
-static struct ast_frame *fax_gateway_framehook(struct ast_channel *chan, struct ast_frame *f, enum ast_framehook_event event, void *data) {
+static struct ast_frame *fax_gateway_framehook(struct ast_channel *chan, struct ast_frame *f, enum ast_framehook_event event, void *data)
+{
 	struct fax_gateway *gateway = data;
 	struct ast_channel *active;
 	RAII_VAR(struct ast_fax_session_details *, details, NULL, ao2_cleanup);
@@ -3287,13 +3283,13 @@ static struct ast_frame *fax_gateway_framehook(struct ast_channel *chan, struct
 
 		if (gateway->bridged) {
 			ast_set_read_format(chan, gateway->chan_read_format);
-			ast_set_read_format(chan, gateway->chan_write_format);
+			ast_set_write_format(chan, gateway->chan_write_format);
 
 			ast_channel_unlock(chan);
 			peer = ast_channel_bridge_peer(chan);
 			if (peer) {
 				ast_set_read_format(peer, gateway->peer_read_format);
-				ast_set_read_format(peer, gateway->peer_write_format);
+				ast_set_write_format(peer, gateway->peer_write_format);
 				ast_channel_make_compatible(chan, peer);
 			}
 			ast_channel_lock(chan);
@@ -3336,23 +3332,25 @@ static struct ast_frame *fax_gateway_framehook(struct ast_channel *chan, struct
 			gateway->timeout_start = ast_tvnow();
 		}
 
+		ast_channel_unlock(chan);
+		ast_channel_lock_both(chan, peer);
+
 		/* we are bridged, change r/w formats to SLIN for v21 preamble
 		 * detection and T.30 */
 		ao2_replace(gateway->chan_read_format, ast_channel_readformat(chan));
-		ao2_replace(gateway->chan_write_format, ast_channel_readformat(chan));
+		ao2_replace(gateway->chan_write_format, ast_channel_writeformat(chan));
 
 		ao2_replace(gateway->peer_read_format, ast_channel_readformat(peer));
-		ao2_replace(gateway->peer_write_format, ast_channel_readformat(peer));
+		ao2_replace(gateway->peer_write_format, ast_channel_writeformat(peer));
 
 		ast_set_read_format(chan, ast_format_slin);
 		ast_set_write_format(chan, ast_format_slin);
 
-		ast_channel_unlock(chan);
 		ast_set_read_format(peer, ast_format_slin);
 		ast_set_write_format(peer, ast_format_slin);
 
-		ast_channel_make_compatible(chan, peer);
-		ast_channel_lock(chan);
+		ast_channel_unlock(peer);
+
 		gateway->bridged = 1;
 	}
 
@@ -3565,7 +3563,8 @@ static struct fax_detect *fax_detect_new(struct ast_channel *chan, int timeout,
 
 /*! \brief Deref the faxdetect data structure when the faxdetect framehook is detached
  * \param data framehook data (faxdetect data)*/
-static void fax_detect_framehook_destroy(void *data) {
+static void fax_detect_framehook_destroy(void *data)
+{
 	struct fax_detect *faxdetect = data;
 
 	ao2_ref(faxdetect, -1);
@@ -3582,7 +3581,8 @@ static void fax_detect_framehook_destroy(void *data) {
  *
  * \return processed frame or NULL when f is NULL or a null frame
  */
-static struct ast_frame *fax_detect_framehook(struct ast_channel *chan, struct ast_frame *f, enum ast_framehook_event event, void *data) {
+static struct ast_frame *fax_detect_framehook(struct ast_channel *chan, struct ast_frame *f, enum ast_framehook_event event, void *data)
+{
 	struct fax_detect *faxdetect = data;
 	struct ast_fax_session_details *details;
 	struct ast_control_t38_parameters *control_params;
@@ -3923,6 +3923,7 @@ static char *cli_fax_show_settings(struct ast_cli_entry *e, int cmd, struct ast_
 	ast_cli(a->fd, "\tMaximum Bit Rate: %u\n", options.maxrate);
 	ast_fax_modem_to_str(options.modems, modems, sizeof(modems));
 	ast_cli(a->fd, "\tModem Modulations Allowed: %s\n", modems);
+	ast_cli(a->fd, "\tT.38 Negotiation Timeout: %u\n", options.t38timeout);
 	ast_cli(a->fd, "\n\nFAX Technology Modules:\n\n");
 	AST_RWLIST_RDLOCK(&faxmodules);
 	AST_RWLIST_TRAVERSE(&faxmodules, fax, list) {
@@ -4179,17 +4180,18 @@ static int manager_fax_sessions_entry(struct mansession *s,
 static int manager_fax_sessions(struct mansession *s, const struct message *m)
 {
 	const char *action_id = astman_get_header(m, "ActionID");
-	char id_text[256] = "";
+	char id_text[256];
 	struct ast_fax_session *session;
 	struct ao2_iterator iter;
 	int session_count = 0;
 
-	astman_send_listack(s, m, "FAXSessionsEntry event list will follow", "Start");
-
+	id_text[0] = '\0';
 	if (!ast_strlen_zero(action_id)) {
 		snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", action_id);
 	}
 
+	astman_send_listack(s, m, "FAXSessionsEntry event list will follow", "Start");
+
 	iter = ao2_iterator_init(faxregistry.container, 0);
 	while ((session = ao2_iterator_next(&iter))) {
 		if (!manager_fax_sessions_entry(s, session, id_text)) {
@@ -4199,13 +4201,9 @@ static int manager_fax_sessions(struct mansession *s, const struct message *m)
 	}
 	ao2_iterator_destroy(&iter);
 
-	astman_append(s, "Event: FAXSessionsComplete\r\n"
-		"%s"
-		"EventList: Complete\r\n"
-		"Total: %d\r\n"
-		"\r\n",
-		id_text,
-		session_count);
+	astman_send_list_complete_start(s, m, "FAXSessionsComplete", session_count);
+	astman_append(s, "Total: %d\r\n", session_count);
+	astman_send_list_complete_end(s);
 
 	return 0;
 }
@@ -4234,6 +4232,23 @@ static void get_general_options(struct fax_options *options)
 	ast_rwlock_unlock(&options_lock);
 }
 
+static int set_t38timeout(const char *value, unsigned int *t38timeout)
+{
+	unsigned int timeout;
+
+	if (sscanf(value, "%u", &timeout) != 1) {
+		ast_log(LOG_ERROR, "Unable to get timeout from '%s'\n", value);
+		return -1;
+	} else if (timeout) {
+		*t38timeout = timeout;
+	} else {
+		ast_log(LOG_ERROR, "T.38 negotiation timeout must be non-zero\n");
+		return -1;
+	}
+
+	return 0;
+}
+
 /*! \brief configure res_fax */
 static int set_config(int reload)
 {
@@ -4303,6 +4318,11 @@ static int set_config(int reload)
 		} else if ((!strcasecmp(v->name, "modem")) || (!strcasecmp(v->name, "modems"))) {
 			options.modems = 0;
 			update_modem_bits(&options.modems, v->value);
+		} else if (!strcasecmp(v->name, "t38timeout")) {
+			if (set_t38timeout(v->value, &options.t38timeout)) {
+				res = -1;
+				goto end;
+			}
 		}
 	}
 
@@ -4395,6 +4415,8 @@ static int acf_faxopt_read(struct ast_channel *chan, const char *cmd, char *data
 		ast_copy_string(buf, details->resultstr, len);
 	} else if ((!strcasecmp(data, "modem")) || (!strcasecmp(data, "modems"))) {
 		ast_fax_modem_to_str(details->modems, buf, len);
+	} else if (!strcasecmp(data, "t38timeout")) {
+		snprintf(buf, len, "%u", details->t38timeout);
 	} else {
 		ast_log(LOG_WARNING, "channel '%s' can't read FAXOPT(%s) because it is unhandled!\n", ast_channel_name(chan), data);
 		res = -1;
@@ -4521,6 +4543,10 @@ static int acf_faxopt_write(struct ast_channel *chan, const char *cmd, char *dat
 		if (!details->minrate) {
 			details->minrate = ast_fax_minrate();
 		}
+	} else if (!strcasecmp(data, "t38timeout")) {
+		if (set_t38timeout(value, &details->t38timeout)) {
+			res = -1;
+		}
 	} else if ((!strcasecmp(data, "modem")) || (!strcasecmp(data, "modems"))) {
 		update_modem_bits(&details->modems, value);
 	} else {
diff --git a/res/res_fax_spandsp.c b/res/res_fax_spandsp.c
index c6bd09b..0152b00 100644
--- a/res/res_fax_spandsp.c
+++ b/res/res_fax_spandsp.c
@@ -50,7 +50,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 423372 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #define SPANDSP_EXPOSE_INTERNAL_STRUCTURES
 #include <spandsp.h>
@@ -502,7 +502,7 @@ static int spandsp_modems(struct ast_fax_session_details *details)
 	if (AST_FAX_MODEM_V17 & details->modems) {
 		modems |= T30_SUPPORT_V17;
 	}
-	if (AST_FAX_MODEM_V27 & details->modems) {
+	if (AST_FAX_MODEM_V27TER & details->modems) {
 		modems |= T30_SUPPORT_V27TER;
 	}
 	if (AST_FAX_MODEM_V29 & details->modems) {
@@ -591,8 +591,10 @@ e_return:
 	return NULL;
 }
 
-static void spandsp_v21_cleanup(struct ast_fax_session *s) {
+static void spandsp_v21_cleanup(struct ast_fax_session *s)
+{
 	struct spandsp_pvt *p = s->tech_pvt;
+
 	modem_connect_tones_rx_free(p->tone_state);
 }
 
@@ -668,7 +670,8 @@ static void spandsp_v21_tone(void *data, int code, int level, int delay)
 	}
 }
 
-static int spandsp_v21_detect(struct ast_fax_session *s, const struct ast_frame *f) {
+static int spandsp_v21_detect(struct ast_fax_session *s, const struct ast_frame *f)
+{
 	struct spandsp_pvt *p = s->tech_pvt;
 	int16_t *slndata;
 	g711_state_t *decoder;
@@ -793,19 +796,22 @@ static int spandsp_fax_gw_t30_gen(struct ast_channel *chan, void *data, int len,
  * \param chan channel
  * \param params generator data
  * \return data to use in generator call*/
-static void *spandsp_fax_gw_gen_alloc(struct ast_channel *chan, void *params) {
+static void *spandsp_fax_gw_gen_alloc(struct ast_channel *chan, void *params)
+{
 	ao2_ref(params, +1);
 	return params;
 }
 
-static void spandsp_fax_gw_gen_release(struct ast_channel *chan, void *data) {
+static void spandsp_fax_gw_gen_release(struct ast_channel *chan, void *data)
+{
 	ao2_ref(data, -1);
 }
 
 /*! \brief activate a spandsp gateway based on the information in the given fax session
  * \param s fax session
  * \return -1 on error 0 on sucess*/
-static int spandsp_fax_gateway_start(struct ast_fax_session *s) {
+static int spandsp_fax_gateway_start(struct ast_fax_session *s)
+{
 	struct spandsp_pvt *p = s->tech_pvt;
 	struct ast_fax_t38_parameters *t38_param;
 	int i;
@@ -830,8 +836,8 @@ static int spandsp_fax_gateway_start(struct ast_fax_session *s) {
 
 	p->ist38 = 1;
 	p->ast_t38_state = ast_channel_get_t38_state(s->chan);
-	if (!(peer = ast_channel_bridge_peer(s->chan))) {
-		ast_channel_unlock(s->chan);
+	peer = ast_channel_bridge_peer(s->chan);
+	if (!peer) {
 		return -1;
 	}
 
diff --git a/res/res_format_attr_celt.c b/res/res_format_attr_celt.c
index 6e3191b..7d7d140 100644
--- a/res/res_format_attr_celt.c
+++ b/res/res_format_attr_celt.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/format.h"
diff --git a/res/res_format_attr_h263.c b/res/res_format_attr_h263.c
index 88bb379..22f1936 100644
--- a/res/res_format_attr_h263.c
+++ b/res/res_format_attr_h263.c
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/format.h"
diff --git a/res/res_format_attr_h264.c b/res/res_format_attr_h264.c
index 37637fa..34793ef 100644
--- a/res/res_format_attr_h264.c
+++ b/res/res_format_attr_h264.c
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/format.h"
@@ -153,13 +153,13 @@ static struct ast_format *h264_getjoint(const struct ast_format *format1, const
 	if (attr1 && !ast_strlen_zero(attr1->SPS)) {
 		ast_copy_string(attr->SPS, attr1->SPS, sizeof(attr->SPS));
 	} else if (attr2 && !ast_strlen_zero(attr2->SPS)) {
-		ast_copy_string(attr->SPS, attr1->SPS, sizeof(attr->SPS));
+		ast_copy_string(attr->SPS, attr2->SPS, sizeof(attr->SPS));
 	}
 
 	if (attr1 && !ast_strlen_zero(attr1->PPS)) {
 		ast_copy_string(attr->PPS, attr1->PPS, sizeof(attr->PPS));
 	} else if (attr2 && !ast_strlen_zero(attr2->PPS)) {
-		ast_copy_string(attr->PPS, attr1->PPS, sizeof(attr->PPS));
+		ast_copy_string(attr->PPS, attr2->PPS, sizeof(attr->PPS));
 	}
 
 	return cloned;
@@ -236,7 +236,7 @@ static struct ast_format *h264_parse_sdp_fmtp(const struct ast_format *format, c
 	if (field != H264_ATTR_KEY_UNSET) {	\
 		if (added) {	\
 			ast_str_append(str, 0, ";");	\
-		} else {	\
+		} else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {	\
 			added = 1;	\
 		}	\
 		ast_str_append(str, 0, "%s=%u", name, field);	\
@@ -247,7 +247,7 @@ static struct ast_format *h264_parse_sdp_fmtp(const struct ast_format *format, c
 	if (field) {	\
 		if (added) {	\
 			ast_str_append(str, 0, ";");	\
-		} else {	\
+		} else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {	\
 			added = 1;	\
 		}	\
 		ast_str_append(str, 0, "%s=%u", name, field);	\
@@ -263,8 +263,6 @@ static void h264_generate_sdp_fmtp(const struct ast_format *format, unsigned int
 		return;
 	}
 
-	ast_str_append(str, 0, "a=fmtp:%u ", payload);
-
 	APPEND_IF_NONZERO(attr->MAX_MBPS, str, "max-mbps");
 	APPEND_IF_NONZERO(attr->MAX_FS, str, "max-fs");
 	APPEND_IF_NONZERO(attr->MAX_CPB, str, "max-cpb");
@@ -287,7 +285,7 @@ static void h264_generate_sdp_fmtp(const struct ast_format *format, unsigned int
 	if (attr->PROFILE_IDC && attr->PROFILE_IOP && attr->LEVEL) {
 		if (added) {
 			ast_str_append(str, 0, ";");
-		} else {
+		} else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
 			added = 1;
 		}
 		ast_str_append(str, 0, "profile-level-id=%02X%02X%02X", attr->PROFILE_IDC, attr->PROFILE_IOP, attr->LEVEL);
@@ -296,15 +294,13 @@ static void h264_generate_sdp_fmtp(const struct ast_format *format, unsigned int
 	if (!ast_strlen_zero(attr->SPS) && !ast_strlen_zero(attr->PPS)) {
 		if (added) {
 			ast_str_append(str, 0, ";");
-		} else {
+		} else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
 			added = 1;
 		}
-		ast_str_append(str, 0, ";sprop-parameter-sets=%s,%s", attr->SPS, attr->PPS);
+		ast_str_append(str, 0, "sprop-parameter-sets=%s,%s", attr->SPS, attr->PPS);
 	}
 
-	if (!added) {
-		ast_str_reset(*str);
-	} else {
+	if (added) {
 		ast_str_append(str, 0, "\r\n");
 	}
 
diff --git a/res/res_format_attr_opus.c b/res/res_format_attr_opus.c
index 65d83e0..4e81df0 100644
--- a/res/res_format_attr_opus.c
+++ b/res/res_format_attr_opus.c
@@ -29,26 +29,40 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/format.h"
+#include "asterisk/logger.h"            /* for ast_log, LOG_WARNING */
+#include "asterisk/strings.h"           /* for ast_str_append */
+#include "asterisk/utils.h"             /* for MIN, ast_malloc, ast_free */
 
 /*!
  * \brief Opus attribute structure.
  *
- * \note http://tools.ietf.org/html/draft-ietf-payload-rtp-opus-00.
+ * \note http://tools.ietf.org/html/rfc7587#section-6
  */
 struct opus_attr {
-	unsigned int maxbitrate;	        /* Default 64-128 kb/s for FB stereo music */
-	unsigned int maxplayrate	        /* Default 48000 */;
-	unsigned int minptime;	            /* Default 3, but it's 10 in format.c */
-	unsigned int stereo;	            /* Default 0 */
-	unsigned int cbr;	                /* Default 0 */
-	unsigned int fec;	                /* Default 0 */
-	unsigned int dtx;	                /* Default 0 */
-	unsigned int spropmaxcapturerate;	/* Default 48000 */
-	unsigned int spropstereo;	        /* Default 0 */
+	unsigned int maxbitrate;
+	unsigned int maxplayrate;
+	unsigned int unused; /* was minptime, kept for binary compatibility */
+	unsigned int stereo;
+	unsigned int cbr;
+	unsigned int fec;
+	unsigned int dtx;
+	unsigned int spropmaxcapturerate;
+	unsigned int spropstereo;
+};
+
+static struct opus_attr default_opus_attr = {
+	.maxplayrate         = 48000,
+	.spropmaxcapturerate = 48000,
+	.maxbitrate          = 510000,
+	.stereo              = 0,
+	.spropstereo         = 0,
+	.cbr                 = 0,
+	.fec                 = 1,
+	.dtx                 = 0,
 };
 
 static void opus_destroy(struct ast_format *format)
@@ -61,7 +75,7 @@ static void opus_destroy(struct ast_format *format)
 static int opus_clone(const struct ast_format *src, struct ast_format *dst)
 {
 	struct opus_attr *original = ast_format_get_attribute_data(src);
-	struct opus_attr *attr = ast_calloc(1, sizeof(*attr));
+	struct opus_attr *attr = ast_malloc(sizeof(*attr));
 
 	if (!attr) {
 		return -1;
@@ -69,6 +83,8 @@ static int opus_clone(const struct ast_format *src, struct ast_format *dst)
 
 	if (original) {
 		*attr = *original;
+	} else {
+		*attr = default_opus_attr;
 	}
 
 	ast_format_set_attribute_data(dst, attr);
@@ -91,69 +107,148 @@ static struct ast_format *opus_parse_sdp_fmtp(const struct ast_format *format, c
 
 	if ((kvp = strstr(attributes, "maxplaybackrate")) && sscanf(kvp, "maxplaybackrate=%30u", &val) == 1) {
 		attr->maxplayrate = val;
+	} else {
+		attr->maxplayrate = 48000;
 	}
+
 	if ((kvp = strstr(attributes, "sprop-maxcapturerate")) && sscanf(kvp, "sprop-maxcapturerate=%30u", &val) == 1) {
 		attr->spropmaxcapturerate = val;
+	} else {
+		attr->spropmaxcapturerate = 48000;
 	}
-	if ((kvp = strstr(attributes, "minptime")) && sscanf(kvp, "minptime=%30u", &val) == 1) {
-		attr->minptime = val;
-	}
+
 	if ((kvp = strstr(attributes, "maxaveragebitrate")) && sscanf(kvp, "maxaveragebitrate=%30u", &val) == 1) {
 		attr->maxbitrate = val;
+	} else {
+		attr->maxbitrate = 510000;
 	}
-	if ((kvp = strstr(attributes, " stereo")) && sscanf(kvp, " stereo=%30u", &val) == 1) {
-		attr->stereo = val;
-	}
-	if ((kvp = strstr(attributes, ";stereo")) && sscanf(kvp, ";stereo=%30u", &val) == 1) {
-		attr->stereo = val;
+
+	if (!strncmp(attributes, "stereo=1", 8)) {
+		attr->stereo = 1;
+	} else if (strstr(attributes, " stereo=1")) {
+		attr->stereo = 1;
+	} else if (strstr(attributes, ";stereo=1")) {
+		attr->stereo = 1;
+	} else {
+		attr->stereo = 0;
 	}
-	if ((kvp = strstr(attributes, "sprop-stereo")) && sscanf(kvp, "sprop-stereo=%30u", &val) == 1) {
-		attr->spropstereo = val;
+
+	if (strstr(attributes, "sprop-stereo=1")) {
+		attr->spropstereo = 1;
+	} else {
+		attr->spropstereo = 0;
 	}
-	if ((kvp = strstr(attributes, "cbr")) && sscanf(kvp, "cbr=%30u", &val) == 1) {
-		attr->cbr = val;
+
+	if (strstr(attributes, "cbr=1")) {
+		attr->cbr = 1;
+	} else {
+		attr->cbr = 0;
 	}
-	if ((kvp = strstr(attributes, "useinbandfec")) && sscanf(kvp, "useinbandfec=%30u", &val) == 1) {
-		attr->fec = val;
+
+	if (strstr(attributes, "useinbandfec=1")) {
+		attr->fec = 1;
+	} else {
+		attr->fec = 0;
 	}
-	if ((kvp = strstr(attributes, "usedtx")) && sscanf(kvp, "usedtx=%30u", &val) == 1) {
-		attr->dtx = val;
+
+	if (strstr(attributes, "usedtx=1")) {
+		attr->dtx = 1;
+	} else {
+		attr->dtx = 0;
 	}
 
-	return 0;
+	return cloned;
 }
 
 static void opus_generate_sdp_fmtp(const struct ast_format *format, unsigned int payload, struct ast_str **str)
 {
 	struct opus_attr *attr = ast_format_get_attribute_data(format);
+	int added = 0;
 
 	if (!attr) {
-		return;
+		/*
+		 * (Only) cached formats do not have attribute data assigned because
+		 * they were created before this attribute module was registered.
+		 * Therefore, we assume the default attribute values here.
+		 */
+		attr = &default_opus_attr;
+	}
+
+	if (48000 != attr->maxplayrate) {
+		if (added) {
+			ast_str_append(str, 0, ";");
+		} else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
+			added = 1;
+		}
+		ast_str_append(str, 0, "maxplaybackrate=%u", attr->maxplayrate);
+	}
+
+	if (48000 != attr->spropmaxcapturerate) {
+		if (added) {
+			ast_str_append(str, 0, ";");
+		} else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
+			added = 1;
+		}
+		ast_str_append(str, 0, "sprop-maxcapturerate=%u", attr->spropmaxcapturerate);
+	}
+
+	if (510000 != attr->maxbitrate) {
+		if (added) {
+			ast_str_append(str, 0, ";");
+		} else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
+			added = 1;
+		}
+		ast_str_append(str, 0, "maxaveragebitrate=%u", attr->maxbitrate);
+	}
+
+	if (0 != attr->stereo) {
+		if (added) {
+			ast_str_append(str, 0, ";");
+		} else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
+			added = 1;
+		}
+		ast_str_append(str, 0, "stereo=%u", attr->stereo);
+	}
+
+	if (0 != attr->spropstereo) {
+		if (added) {
+			ast_str_append(str, 0, ";");
+		} else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
+			added = 1;
+		}
+		ast_str_append(str, 0, "sprop-stereo=%u", attr->spropstereo);
 	}
 
-	/* FIXME should we only generate attributes that were explicitly set? */
-	ast_str_append(str, 0,
-				"a=fmtp:%u "
-					"maxplaybackrate=%u;"
-					"sprop-maxcapturerate=%u;"
-					"minptime=%u;"
-					"maxaveragebitrate=%u;"
-					"stereo=%d;"
-					"sprop-stereo=%d;"
-					"cbr=%d;"
-					"useinbandfec=%d;"
-					"usedtx=%d\r\n",
-			payload,
-			attr->maxplayrate ? attr->maxplayrate : 48000,	/* maxplaybackrate */
-			attr->spropmaxcapturerate ? attr->spropmaxcapturerate : 48000,	/* sprop-maxcapturerate */
-			attr->minptime > 10 ? attr->minptime : 10,	/* minptime */
-			attr->maxbitrate ? attr->maxbitrate : 20000,	/* maxaveragebitrate */
-			attr->stereo ? 1 : 0,		/* stereo */
-			attr->spropstereo ? 1 : 0,		/* sprop-stereo */
-			attr->cbr ? 1 : 0,		/* cbr */
-			attr->fec ? 1 : 0,		/* useinbandfec */
-			attr->dtx ? 1 : 0		/* usedtx */
-	);
+	if (0 != attr->cbr) {
+		if (added) {
+			ast_str_append(str, 0, ";");
+		} else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
+			added = 1;
+		}
+		ast_str_append(str, 0, "cbr=%u", attr->cbr);
+	}
+
+	if (0 != attr->fec) {
+		if (added) {
+			ast_str_append(str, 0, ";");
+		} else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
+			added = 1;
+		}
+		ast_str_append(str, 0, "useinbandfec=%u", attr->fec);
+	}
+
+	if (0 != attr->dtx) {
+		if (added) {
+			ast_str_append(str, 0, ";");
+		} else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
+			added = 1;
+		}
+		ast_str_append(str, 0, "usedtx=%u", attr->dtx);
+	}
+
+	if (added) {
+		ast_str_append(str, 0, "\r\n");
+	}
 }
 
 static struct ast_format *opus_getjoint(const struct ast_format *format1, const struct ast_format *format2)
@@ -163,25 +258,36 @@ static struct ast_format *opus_getjoint(const struct ast_format *format1, const
 	struct ast_format *jointformat;
 	struct opus_attr *attr_res;
 
+	if (!attr1) {
+		attr1 = &default_opus_attr;
+	}
+
+	if (!attr2) {
+		attr2 = &default_opus_attr;
+	}
+
 	jointformat = ast_format_clone(format1);
 	if (!jointformat) {
 		return NULL;
 	}
 	attr_res = ast_format_get_attribute_data(jointformat);
 
-	/* Only do dtx if both sides want it. DTX is a trade off between
-	 * computational complexity and bandwidth. */
-	attr_res->dtx = attr1->dtx && attr2->dtx ? 1 : 0;
+	attr_res->dtx = attr1->dtx || attr2->dtx ? 1 : 0;
 
 	/* Only do FEC if both sides want it.  If a peer specifically requests not
 	 * to receive with FEC, it may be a waste of bandwidth. */
 	attr_res->fec = attr1->fec && attr2->fec ? 1 : 0;
 
+	attr_res->cbr = attr1->cbr || attr2->cbr ? 1 : 0;
+	attr_res->spropstereo = attr1->spropstereo || attr2->spropstereo ? 1 : 0;
+
 	/* Only do stereo if both sides want it.  If a peer specifically requests not
 	 * to receive stereo signals, it may be a waste of bandwidth. */
 	attr_res->stereo = attr1->stereo && attr2->stereo ? 1 : 0;
 
-	/* FIXME: do we need to join other attributes as well, e.g., minptime, cbr, etc.? */
+	attr_res->maxbitrate = MIN(attr1->maxbitrate, attr2->maxbitrate);
+	attr_res->spropmaxcapturerate = MIN(attr1->spropmaxcapturerate, attr2->spropmaxcapturerate);
+	attr_res->maxplayrate = MIN(attr1->maxplayrate, attr2->maxplayrate);
 
 	return jointformat;
 }
@@ -209,7 +315,7 @@ static struct ast_format *opus_set(const struct ast_format *format, const char *
 	} else if (!strcasecmp(name, "max_playrate")) {
 		attr->maxplayrate = val;
 	} else if (!strcasecmp(name, "minptime")) {
-		attr->minptime = val;
+		attr->unused = val;
 	} else if (!strcasecmp(name, "stereo")) {
 		attr->stereo = val;
 	} else if (!strcasecmp(name, "cbr")) {
diff --git a/res/res_format_attr_silk.c b/res/res_format_attr_silk.c
index 766439a..dcbbe4c 100644
--- a/res/res_format_attr_silk.c
+++ b/res/res_format_attr_silk.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/format.h"
@@ -94,7 +94,7 @@ static struct ast_format *silk_parse_sdp_fmtp(const struct ast_format *format, c
 		attr->fec = val;
 	}
 
-	return 0;
+	return cloned;
 }
 
 static void silk_generate_sdp_fmtp(const struct ast_format *format, unsigned int payload, struct ast_str **str)
@@ -200,12 +200,36 @@ static struct ast_format *silk_set(const struct ast_format *format, const char *
 	return cloned;
 }
 
+static const void *silk_get(const struct ast_format *format, const char *name)
+{
+	struct silk_attr *attr = ast_format_get_attribute_data(format);
+	unsigned int *val;
+
+	if (!strcasecmp(name, "sample_rate")) {
+		val = &attr->samplerate;
+	} else if (!strcasecmp(name, "max_bitrate")) {
+		val = &attr->maxbitrate;
+	} else if (!strcasecmp(name, "dtx")) {
+		val = &attr->dtx;
+	} else if (!strcasecmp(name, "fec")) {
+		val = &attr->fec;
+	} else if (!strcasecmp(name, "packetloss_percentage")) {
+		val = &attr->packetloss_percentage;
+	} else {
+		ast_log(LOG_WARNING, "unknown attribute type %s\n", name);
+		return NULL;
+	}
+
+	return val;
+}
+
 static struct ast_format_interface silk_interface = {
 	.format_destroy = silk_destroy,
 	.format_clone = silk_clone,
 	.format_cmp = silk_cmp,
 	.format_get_joint = silk_getjoint,
 	.format_attribute_set = silk_set,
+	.format_attribute_get = silk_get,
 	.format_parse_sdp_fmtp = silk_parse_sdp_fmtp,
 	.format_generate_sdp_fmtp = silk_generate_sdp_fmtp,
 };
diff --git a/res/res_format_attr_vp8.c b/res/res_format_attr_vp8.c
new file mode 100644
index 0000000..78a6889
--- /dev/null
+++ b/res/res_format_attr_vp8.c
@@ -0,0 +1,228 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2015, Alexander Traud
+ *
+ * Alexander Traud <pabstraud at compuserve.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ * \brief VP8 format attribute interface
+ *
+ * \author Alexander Traud <pabstraud at compuserve.com>
+ *
+ * \note http://tools.ietf.org/html/draft-ietf-payload-vp8
+ */
+
+/*** MODULEINFO
+	<support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/format.h"
+#include "asterisk/logger.h"            /* for ast_log, LOG_WARNING */
+#include "asterisk/strings.h"           /* for ast_str_append */
+#include "asterisk/utils.h"             /* for MIN, ast_malloc, ast_free */
+
+struct vp8_attr {
+	unsigned int maximum_frame_rate;
+	unsigned int maximum_frame_size;
+};
+
+static struct vp8_attr default_vp8_attr = {
+	.maximum_frame_rate = UINT_MAX,
+	.maximum_frame_size = UINT_MAX,
+};
+
+static void vp8_destroy(struct ast_format *format)
+{
+	struct vp8_attr *attr = ast_format_get_attribute_data(format);
+
+	ast_free(attr);
+}
+
+static int vp8_clone(const struct ast_format *src, struct ast_format *dst)
+{
+	struct vp8_attr *original = ast_format_get_attribute_data(src);
+	struct vp8_attr *attr = ast_malloc(sizeof(*attr));
+
+	if (!attr) {
+		return -1;
+	}
+
+	if (original) {
+		*attr = *original;
+	} else {
+		*attr = default_vp8_attr;
+	}
+
+	ast_format_set_attribute_data(dst, attr);
+
+	return 0;
+}
+
+static struct ast_format *vp8_parse_sdp_fmtp(const struct ast_format *format, const char *attributes)
+{
+	struct ast_format *cloned;
+	struct vp8_attr *attr;
+	const char *kvp;
+	unsigned int val;
+
+	cloned = ast_format_clone(format);
+	if (!cloned) {
+		return NULL;
+	}
+	attr = ast_format_get_attribute_data(cloned);
+
+	if ((kvp = strstr(attributes, "max-fr")) && sscanf(kvp, "max-fr=%30u", &val) == 1) {
+		attr->maximum_frame_rate = val;
+	} else {
+		attr->maximum_frame_rate = UINT_MAX;
+	}
+
+	if ((kvp = strstr(attributes, "max-fs")) && sscanf(kvp, "max-fs=%30u", &val) == 1) {
+		attr->maximum_frame_size = val;
+	} else {
+		attr->maximum_frame_size = UINT_MAX;
+	}
+
+	return cloned;
+}
+
+static void vp8_generate_sdp_fmtp(const struct ast_format *format, unsigned int payload, struct ast_str **str)
+{
+	struct vp8_attr *attr = ast_format_get_attribute_data(format);
+	int added = 0;
+
+	if (!attr) {
+		/*
+		 * (Only) cached formats do not have attribute data assigned because
+		 * they were created before this attribute module was registered.
+		 * Therefore, we assume the default attribute values here.
+		 */
+		attr = &default_vp8_attr;
+	}
+
+	if (UINT_MAX != attr->maximum_frame_rate) {
+		if (added) {
+			ast_str_append(str, 0, ";");
+		} else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
+			added = 1;
+		}
+		ast_str_append(str, 0, "max-fr=%u", attr->maximum_frame_rate);
+	}
+
+	if (UINT_MAX != attr->maximum_frame_size) {
+		if (added) {
+			ast_str_append(str, 0, ";");
+		} else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
+			added = 1;
+		}
+		ast_str_append(str, 0, "max-fs=%u", attr->maximum_frame_size);
+	}
+
+	if (added) {
+		ast_str_append(str, 0, "\r\n");
+	}
+}
+
+static struct ast_format *vp8_getjoint(const struct ast_format *format1, const struct ast_format *format2)
+{
+	struct vp8_attr *attr1 = ast_format_get_attribute_data(format1);
+	struct vp8_attr *attr2 = ast_format_get_attribute_data(format2);
+	struct ast_format *jointformat;
+	struct vp8_attr *attr_res;
+
+	if (!attr1) {
+		attr1 = &default_vp8_attr;
+	}
+
+	if (!attr2) {
+		attr2 = &default_vp8_attr;
+	}
+
+	jointformat = ast_format_clone(format1);
+	if (!jointformat) {
+		return NULL;
+	}
+	attr_res = ast_format_get_attribute_data(jointformat);
+
+	attr_res->maximum_frame_rate = MIN(attr1->maximum_frame_rate, attr2->maximum_frame_rate);
+	attr_res->maximum_frame_size = MIN(attr1->maximum_frame_size, attr2->maximum_frame_size);
+
+	return jointformat;
+}
+
+static struct ast_format *vp8_set(const struct ast_format *format, const char *name, const char *value)
+{
+	struct ast_format *cloned;
+	struct vp8_attr *attr;
+	unsigned int val;
+
+	if (sscanf(value, "%30u", &val) != 1) {
+		ast_log(LOG_WARNING, "Unknown value '%s' for attribute type '%s'\n",
+			value, name);
+		return NULL;
+	}
+
+	cloned = ast_format_clone(format);
+	if (!cloned) {
+		return NULL;
+	}
+	attr = ast_format_get_attribute_data(cloned);
+
+	if (!strcasecmp(name, "maximum_frame_rate")) {
+		attr->maximum_frame_rate = val;
+	} else if (!strcasecmp(name, "maximum_frame_size")) {
+		attr->maximum_frame_size = val;
+	} else {
+		ast_log(LOG_WARNING, "unknown attribute type %s\n", name);
+	}
+
+	return cloned;
+}
+
+static struct ast_format_interface vp8_interface = {
+	.format_destroy = vp8_destroy,
+	.format_clone = vp8_clone,
+	.format_get_joint = vp8_getjoint,
+	.format_attribute_set = vp8_set,
+	.format_parse_sdp_fmtp = vp8_parse_sdp_fmtp,
+	.format_generate_sdp_fmtp = vp8_generate_sdp_fmtp,
+};
+
+static int load_module(void)
+{
+	if (ast_format_interface_register("vp8", &vp8_interface)) {
+		return AST_MODULE_LOAD_DECLINE;
+	}
+
+	return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+	return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "VP8 Format Attribute Module",
+	.support_level = AST_MODULE_SUPPORT_CORE,
+	.load = load_module,
+	.unload = unload_module,
+	.load_pri = AST_MODPRI_CHANNEL_DEPEND,
+);
diff --git a/res/res_hep.c b/res/res_hep.c
index 273da5b..69a8ab3 100644
--- a/res/res_hep.c
+++ b/res/res_hep.c
@@ -76,7 +76,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 427405 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/astobj2.h"
diff --git a/res/res_hep_pjsip.c b/res/res_hep_pjsip.c
index 0127377..b5cf0b8 100644
--- a/res/res_hep_pjsip.c
+++ b/res/res_hep_pjsip.c
@@ -34,7 +34,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 425691 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <pjsip.h>
 #include <pjsip_ua.h>
diff --git a/res/res_hep_rtcp.c b/res/res_hep_rtcp.c
index f254b66..787512b 100644
--- a/res/res_hep_rtcp.c
+++ b/res/res_hep_rtcp.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 421065 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/res_hep.h"
 #include "asterisk/module.h"
@@ -131,7 +131,7 @@ static int load_module(void)
 static int unload_module(void)
 {
 	if (stasis_rtp_subscription) {
-		stasis_rtp_subscription = stasis_unsubscribe(stasis_rtp_subscription);
+		stasis_rtp_subscription = stasis_unsubscribe_and_join(stasis_rtp_subscription);
 	}
 
 	return 0;
diff --git a/res/res_http_post.c b/res/res_http_post.c
index 4f1fb13..2ee792a 100644
--- a/res/res_http_post.c
+++ b/res/res_http_post.c
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/stat.h>
 #include <fcntl.h>
diff --git a/res/res_http_websocket.c b/res/res_http_websocket.c
index dac32a2..b6baa3c 100644
--- a/res/res_http_websocket.c
+++ b/res/res_http_websocket.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 429317 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/http.h"
@@ -52,7 +52,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 429317 $")
 #define MAX_PROTOCOL_BUCKETS 7
 
 /*! \brief Size of the pre-determined buffer for WebSocket frames */
-#define MAXIMUM_FRAME_SIZE 8192
+#define MAXIMUM_FRAME_SIZE 16384
 
 /*! \brief Default reconstruction size for multi-frame payload reconstruction. If exceeded the next frame will start a
  *         payload.
@@ -88,16 +88,10 @@ struct ast_websocket {
 	struct websocket_client *client;  /*!< Client object when connected as a client websocket */
 };
 
-/*! \brief Structure definition for protocols */
-struct websocket_protocol {
-	char *name;                      /*!< Name of the protocol */
-	ast_websocket_callback callback; /*!< Callback called when a new session is established */
-};
-
 /*! \brief Hashing function for protocols */
 static int protocol_hash_fn(const void *obj, const int flags)
 {
-	const struct websocket_protocol *protocol = obj;
+	const struct ast_websocket_protocol *protocol = obj;
 	const char *name = obj;
 
 	return ast_str_case_hash(flags & OBJ_KEY ? name : protocol->name);
@@ -106,7 +100,7 @@ static int protocol_hash_fn(const void *obj, const int flags)
 /*! \brief Comparison function for protocols */
 static int protocol_cmp_fn(void *obj, void *arg, int flags)
 {
-	const struct websocket_protocol *protocol1 = obj, *protocol2 = arg;
+	const struct ast_websocket_protocol *protocol1 = obj, *protocol2 = arg;
 	const char *protocol = arg;
 
 	return !strcasecmp(protocol1->name, flags & OBJ_KEY ? protocol : protocol2->name) ? CMP_MATCH | CMP_STOP : 0;
@@ -115,7 +109,7 @@ static int protocol_cmp_fn(void *obj, void *arg, int flags)
 /*! \brief Destructor function for protocols */
 static void protocol_destroy_fn(void *obj)
 {
-	struct websocket_protocol *protocol = obj;
+	struct ast_websocket_protocol *protocol = obj;
 	ast_free(protocol->name);
 }
 
@@ -173,63 +167,101 @@ static void session_destroy_fn(void *obj)
 
 	if (session->f) {
 		ast_websocket_close(session, 0);
-		fclose(session->f);
-		ast_verb(2, "WebSocket connection %s '%s' closed\n", session->client ? "to" : "from",
-			ast_sockaddr_stringify(&session->address));
+		if (session->f) {
+			fclose(session->f);
+			ast_verb(2, "WebSocket connection %s '%s' closed\n", session->client ? "to" : "from",
+				ast_sockaddr_stringify(&session->address));
+		}
 	}
 
 	ao2_cleanup(session->client);
 	ast_free(session->payload);
 }
 
+struct ast_websocket_protocol *AST_OPTIONAL_API_NAME(ast_websocket_sub_protocol_alloc)(const char *name)
+{
+	struct ast_websocket_protocol *protocol;
+
+	protocol = ao2_alloc(sizeof(*protocol), protocol_destroy_fn);
+	if (!protocol) {
+		return NULL;
+	}
+
+	protocol->name = ast_strdup(name);
+	if (!protocol->name) {
+		ao2_ref(protocol, -1);
+		return NULL;
+	}
+	protocol->version = AST_WEBSOCKET_PROTOCOL_VERSION;
+
+	return protocol;
+}
+
 int AST_OPTIONAL_API_NAME(ast_websocket_server_add_protocol)(struct ast_websocket_server *server, const char *name, ast_websocket_callback callback)
 {
-	struct websocket_protocol *protocol;
+	struct ast_websocket_protocol *protocol;
 
 	if (!server->protocols) {
 		return -1;
 	}
 
-	ao2_lock(server->protocols);
+	protocol = ast_websocket_sub_protocol_alloc(name);
+	if (!protocol) {
+		return -1;
+	}
+	protocol->session_established = callback;
 
-	/* Ensure a second protocol handler is not registered for the same protocol */
-	if ((protocol = ao2_find(server->protocols, name, OBJ_KEY | OBJ_NOLOCK))) {
+	if (ast_websocket_server_add_protocol2(server, protocol)) {
 		ao2_ref(protocol, -1);
-		ao2_unlock(server->protocols);
 		return -1;
 	}
 
-	if (!(protocol = ao2_alloc(sizeof(*protocol), protocol_destroy_fn))) {
-		ao2_unlock(server->protocols);
+	return 0;
+}
+
+int AST_OPTIONAL_API_NAME(ast_websocket_server_add_protocol2)(struct ast_websocket_server *server, struct ast_websocket_protocol *protocol)
+{
+	struct ast_websocket_protocol *existing;
+
+	if (!server->protocols) {
 		return -1;
 	}
 
-	if (!(protocol->name = ast_strdup(name))) {
-		ao2_ref(protocol, -1);
-		ao2_unlock(server->protocols);
+	if (protocol->version != AST_WEBSOCKET_PROTOCOL_VERSION) {
+		ast_log(LOG_WARNING, "WebSocket could not register sub-protocol '%s': "
+			"expected version '%u', got version '%u'\n",
+			protocol->name, AST_WEBSOCKET_PROTOCOL_VERSION, protocol->version);
 		return -1;
 	}
 
-	protocol->callback = callback;
+	ao2_lock(server->protocols);
+
+	/* Ensure a second protocol handler is not registered for the same protocol */
+	existing = ao2_find(server->protocols, protocol->name, OBJ_KEY | OBJ_NOLOCK);
+	if (existing) {
+		ao2_ref(existing, -1);
+		ao2_unlock(server->protocols);
+		return -1;
+	}
 
 	ao2_link_flags(server->protocols, protocol, OBJ_NOLOCK);
 	ao2_unlock(server->protocols);
-	ao2_ref(protocol, -1);
 
-	ast_verb(2, "WebSocket registered sub-protocol '%s'\n", name);
+	ast_verb(2, "WebSocket registered sub-protocol '%s'\n", protocol->name);
+	ao2_ref(protocol, -1);
 
 	return 0;
 }
 
 int AST_OPTIONAL_API_NAME(ast_websocket_server_remove_protocol)(struct ast_websocket_server *server, const char *name, ast_websocket_callback callback)
 {
-	struct websocket_protocol *protocol;
+	struct ast_websocket_protocol *protocol;
 
 	if (!(protocol = ao2_find(server->protocols, name, OBJ_KEY))) {
 		return -1;
 	}
 
-	if (protocol->callback != callback) {
+	if (protocol->session_established != callback) {
 		ao2_ref(protocol, -1);
 		return -1;
 	}
@@ -263,17 +295,49 @@ int AST_OPTIONAL_API_NAME(ast_websocket_close)(struct ast_websocket *session, ui
 
 	ao2_lock(session);
 	res = ast_careful_fwrite(session->f, session->fd, frame, 4, session->timeout);
+
+	/* If an error occurred when trying to close this connection explicitly terminate it now.
+	 * Doing so will cause the thread polling on it to wake up and terminate.
+	 */
+	if (res) {
+		fclose(session->f);
+		session->f = NULL;
+		ast_verb(2, "WebSocket connection %s '%s' forcefully closed due to fatal write error\n",
+			session->client ? "to" : "from", ast_sockaddr_stringify(&session->address));
+	}
+
 	ao2_unlock(session);
 	return res;
 }
 
+static const char *opcode_map[] = {
+	[AST_WEBSOCKET_OPCODE_CONTINUATION] = "continuation",
+	[AST_WEBSOCKET_OPCODE_TEXT] = "text",
+	[AST_WEBSOCKET_OPCODE_BINARY] = "binary",
+	[AST_WEBSOCKET_OPCODE_CLOSE] = "close",
+	[AST_WEBSOCKET_OPCODE_PING] = "ping",
+	[AST_WEBSOCKET_OPCODE_PONG] = "pong",
+};
+
+static const char *websocket_opcode2str(enum ast_websocket_opcode opcode)
+{
+	if (opcode < AST_WEBSOCKET_OPCODE_CONTINUATION ||
+			opcode > AST_WEBSOCKET_OPCODE_PONG) {
+		return "<unknown>";
+	} else {
+		return opcode_map[opcode];
+	}
+}
 
 /*! \brief Write function for websocket traffic */
 int AST_OPTIONAL_API_NAME(ast_websocket_write)(struct ast_websocket *session, enum ast_websocket_opcode opcode, char *payload, uint64_t actual_length)
 {
 	size_t header_size = 2; /* The minimum size of a websocket frame is 2 bytes */
 	char *frame;
-	uint64_t length = 0;
+	uint64_t length;
+
+	ast_debug(3, "Writing websocket %s frame, length %" PRIu64 "\n",
+			websocket_opcode2str(opcode), actual_length);
 
 	if (actual_length < 126) {
 		length = actual_length;
@@ -288,7 +352,7 @@ int AST_OPTIONAL_API_NAME(ast_websocket_write)(struct ast_websocket *session, en
 	}
 
 	frame = ast_alloca(header_size);
-	memset(frame, 0, sizeof(*frame));
+	memset(frame, 0, header_size);
 
 	frame[0] = opcode | 0x80;
 	frame[1] = length;
@@ -297,7 +361,7 @@ int AST_OPTIONAL_API_NAME(ast_websocket_write)(struct ast_websocket *session, en
 	if (length == 126) {
 		put_unaligned_uint16(&frame[2], htons(actual_length));
 	} else if (length == 127) {
-		put_unaligned_uint64(&frame[2], htonl(actual_length));
+		put_unaligned_uint64(&frame[2], htonll(actual_length));
 	}
 
 	ao2_lock(session);
@@ -307,11 +371,15 @@ int AST_OPTIONAL_API_NAME(ast_websocket_write)(struct ast_websocket *session, en
 	}
 	if (ast_careful_fwrite(session->f, session->fd, frame, header_size, session->timeout)) {
 		ao2_unlock(session);
+		/* 1011 - server terminating connection due to not being able to fulfill the request */
+		ast_websocket_close(session, 1011);
 		return -1;
 	}
 
 	if (ast_careful_fwrite(session->f, session->fd, payload, actual_length, session->timeout)) {
 		ao2_unlock(session);
+		/* 1011 - server terminating connection due to not being able to fulfill the request */
+		ast_websocket_close(session, 1011);
 		return -1;
 	}
 	fflush(session->f);
@@ -409,18 +477,45 @@ int AST_OPTIONAL_API_NAME(ast_websocket_set_timeout)(struct ast_websocket *sessi
  */
 static inline int ws_safe_read(struct ast_websocket *session, char *buf, int len, enum ast_websocket_opcode *opcode)
 {
-	int sanity;
 	size_t rlen;
 	int xlen = len;
 	char *rbuf = buf;
-	for (sanity = 10; sanity; sanity--) {
+	int sanity = 10;
+
+	ao2_lock(session);
+	if (!session->f) {
+		ao2_unlock(session);
+		errno = ECONNABORTED;
+		return -1;
+	}
+
+	for (;;) {
 		clearerr(session->f);
 		rlen = fread(rbuf, 1, xlen, session->f);
-		if (!rlen && ferror(session->f) && errno != EAGAIN) {
-			ast_log(LOG_ERROR, "Error reading from web socket: %s\n", strerror(errno));
-			*opcode = AST_WEBSOCKET_OPCODE_CLOSE;
-			session->closing = 1;
-			return -1;
+		if (!rlen) {
+			if (feof(session->f)) {
+				ast_log(LOG_WARNING, "Web socket closed abruptly\n");
+				*opcode = AST_WEBSOCKET_OPCODE_CLOSE;
+				session->closing = 1;
+				ao2_unlock(session);
+				return -1;
+			}
+
+			if (ferror(session->f) && errno != EAGAIN) {
+				ast_log(LOG_ERROR, "Error reading from web socket: %s\n", strerror(errno));
+				*opcode = AST_WEBSOCKET_OPCODE_CLOSE;
+				session->closing = 1;
+				ao2_unlock(session);
+				return -1;
+			}
+
+			if (!--sanity) {
+				ast_log(LOG_WARNING, "Websocket seems unresponsive, disconnecting ...\n");
+				*opcode = AST_WEBSOCKET_OPCODE_CLOSE;
+				session->closing = 1;
+				ao2_unlock(session);
+				return -1;
+			}
 		}
 		xlen = xlen - rlen;
 		rbuf = rbuf + rlen;
@@ -431,15 +526,12 @@ static inline int ws_safe_read(struct ast_websocket *session, char *buf, int len
 			ast_log(LOG_ERROR, "ast_wait_for_input returned err: %s\n", strerror(errno));
 			*opcode = AST_WEBSOCKET_OPCODE_CLOSE;
 			session->closing = 1;
+			ao2_unlock(session);
 			return -1;
 		}
 	}
-	if (!sanity) {
-		ast_log(LOG_WARNING, "Websocket seems unresponsive, disconnecting ...\n");
-		*opcode = AST_WEBSOCKET_OPCODE_CLOSE;
-		session->closing = 1;
-		return -1;
-	}
+
+	ao2_unlock(session);
 	return 0;
 }
 
@@ -456,7 +548,7 @@ int AST_OPTIONAL_API_NAME(ast_websocket_read)(struct ast_websocket *session, cha
 	*fragmented = 0;
 
 	if (ws_safe_read(session, &buf[0], MIN_WS_HDR_SZ, opcode)) {
-		return 0;
+		return -1;
 	}
 	frame_size += MIN_WS_HDR_SZ;
 
@@ -474,7 +566,7 @@ int AST_OPTIONAL_API_NAME(ast_websocket_read)(struct ast_websocket *session, cha
 		if (options_len) {
 			/* read the rest of the header options */
 			if (ws_safe_read(session, &buf[frame_size], options_len, opcode)) {
-				return 0;
+				return -1;
 			}
 			frame_size += options_len;
 		}
@@ -503,7 +595,7 @@ int AST_OPTIONAL_API_NAME(ast_websocket_read)(struct ast_websocket *session, cha
 		}
 
 		if (ws_safe_read(session, *payload, *payload_len, opcode)) {
-			return 0;
+			return -1;
 		}
 		/* If a mask is present unmask the payload */
 		if (mask_present) {
@@ -526,7 +618,7 @@ int AST_OPTIONAL_API_NAME(ast_websocket_read)(struct ast_websocket *session, cha
 					session->payload, session->payload_len, *payload_len);
 				*payload_len = 0;
 				ast_websocket_close(session, 1009);
-				return 0;
+				return -1;
 			}
 
 			session->payload = new_payload;
@@ -563,7 +655,7 @@ int AST_OPTIONAL_API_NAME(ast_websocket_read)(struct ast_websocket *session, cha
 		/* Make the payload available so the user can look at the reason code if they so desire */
 		if ((*payload_len) && (new_payload = ast_realloc(session->payload, *payload_len))) {
 			if (ws_safe_read(session, &buf[frame_size], (*payload_len), opcode)) {
-				return 0;
+				return -1;
 			}
 			session->payload = new_payload;
 			memcpy(session->payload, &buf[frame_size], *payload_len);
@@ -585,7 +677,7 @@ int AST_OPTIONAL_API_NAME(ast_websocket_read)(struct ast_websocket *session, cha
 /*!
  * \brief If the server has exactly one configured protocol, return it.
  */
-static struct websocket_protocol *one_protocol(
+static struct ast_websocket_protocol *one_protocol(
 	struct ast_websocket_server *server)
 {
 	SCOPED_AO2LOCK(lock, server->protocols);
@@ -628,7 +720,7 @@ int AST_OPTIONAL_API_NAME(ast_websocket_uri_cb)(struct ast_tcptls_session_instan
 	struct ast_variable *v;
 	char *upgrade = NULL, *key = NULL, *key1 = NULL, *key2 = NULL, *protos = NULL, *requested_protocols = NULL, *protocol = NULL;
 	int version = 0, flags = 1;
-	struct websocket_protocol *protocol_handler = NULL;
+	struct ast_websocket_protocol *protocol_handler = NULL;
 	struct ast_websocket *session;
 	struct ast_websocket_server *server;
 
@@ -727,12 +819,14 @@ int AST_OPTIONAL_API_NAME(ast_websocket_uri_cb)(struct ast_tcptls_session_instan
 		}
 		session->timeout =  AST_DEFAULT_WEBSOCKET_WRITE_TIMEOUT;
 
-		fprintf(ser->f, "HTTP/1.1 101 Switching Protocols\r\n"
-			"Upgrade: %s\r\n"
-			"Connection: Upgrade\r\n"
-			"Sec-WebSocket-Accept: %s\r\n",
-			upgrade,
-			websocket_combine_key(key, base64, sizeof(base64)));
+		if (protocol_handler->session_attempted
+		    && protocol_handler->session_attempted(ser, get_vars, headers)) {
+			ast_debug(3, "WebSocket connection from '%s' rejected by protocol handler '%s'\n",
+				ast_sockaddr_stringify(&ser->remote_address), protocol_handler->name);
+			websocket_bad_request(ser);
+			ao2_ref(protocol_handler, -1);
+			return 0;
+		}
 
 		/* RFC 6455, Section 4.1:
 		 *
@@ -744,11 +838,23 @@ int AST_OPTIONAL_API_NAME(ast_websocket_uri_cb)(struct ast_tcptls_session_instan
 		 *    Connection_.
 		 */
 		if (protocol) {
-			fprintf(ser->f, "Sec-WebSocket-Protocol: %s\r\n",
+			fprintf(ser->f, "HTTP/1.1 101 Switching Protocols\r\n"
+				"Upgrade: %s\r\n"
+				"Connection: Upgrade\r\n"
+				"Sec-WebSocket-Accept: %s\r\n"
+				"Sec-WebSocket-Protocol: %s\r\n\r\n",
+				upgrade,
+				websocket_combine_key(key, base64, sizeof(base64)),
 				protocol);
+		} else {
+			fprintf(ser->f, "HTTP/1.1 101 Switching Protocols\r\n"
+				"Upgrade: %s\r\n"
+				"Connection: Upgrade\r\n"
+				"Sec-WebSocket-Accept: %s\r\n\r\n",
+				upgrade,
+				websocket_combine_key(key, base64, sizeof(base64)));
 		}
 
-		fprintf(ser->f, "\r\n");
 		fflush(ser->f);
 	} else {
 
@@ -782,7 +888,7 @@ int AST_OPTIONAL_API_NAME(ast_websocket_uri_cb)(struct ast_tcptls_session_instan
 
 	/* Give up ownership of the socket and pass it to the protocol handler */
 	ast_tcptls_stream_set_exclusive_input(ser->stream_cookie, 0);
-	protocol_handler->callback(session, get_vars, headers);
+	protocol_handler->session_established(session, get_vars, headers);
 	ao2_ref(protocol_handler, -1);
 
 	/*
@@ -866,6 +972,22 @@ int AST_OPTIONAL_API_NAME(ast_websocket_add_protocol)(const char *name, ast_webs
 	return res;
 }
 
+int AST_OPTIONAL_API_NAME(ast_websocket_add_protocol2)(struct ast_websocket_protocol *protocol)
+{
+	struct ast_websocket_server *ws_server = websocketuri.data;
+
+	if (!ws_server) {
+		return -1;
+	}
+
+	if (ast_websocket_server_add_protocol2(ws_server, protocol)) {
+		return -1;
+	}
+
+	ast_module_ref(ast_module_info->self);
+	return 0;
+}
+
 static int websocket_remove_protocol_internal(const char *name, ast_websocket_callback callback)
 {
 	struct ast_websocket_server *ws_server = websocketuri.data;
@@ -1279,8 +1401,19 @@ int AST_OPTIONAL_API_NAME(ast_websocket_read_string)
 int AST_OPTIONAL_API_NAME(ast_websocket_write_string)
 	(struct ast_websocket *ws, const char *buf)
 {
+	uint64_t len = strlen(buf);
+
+	ast_debug(3, "Writing websocket string of length %" PRIu64 "\n", len);
+
+	/* We do not pass strlen(buf) to ast_websocket_write() directly because the
+	 * size_t returned by strlen() may not require the same storage size
+	 * as the uint64_t that ast_websocket_write() uses. This normally
+	 * would not cause a problem, but since ast_websocket_write() uses
+	 * the optional API, this function call goes through a series of macros
+	 * that may cause a 32-bit to 64-bit conversion to go awry.
+	 */
 	return ast_websocket_write(ws, AST_WEBSOCKET_OPCODE_TEXT,
-				   (char *)buf, strlen(buf));
+				   (char *)buf, len);
 }
 
 static int load_module(void)
diff --git a/res/res_limit.c b/res/res_limit.c
index d3f7445..e883ff3 100644
--- a/res/res_limit.c
+++ b/res/res_limit.c
@@ -24,7 +24,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 375003 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <ctype.h>
 #include <sys/time.h>
diff --git a/res/res_manager_devicestate.c b/res/res_manager_devicestate.c
index 25eae87..3d1f1ab 100644
--- a/res/res_manager_devicestate.c
+++ b/res/res_manager_devicestate.c
@@ -106,12 +106,8 @@ static int action_devicestatelist(struct mansession *s, const struct message *m)
 	}
 	ao2_iterator_destroy(&it_states);
 
-	astman_append(s, "Event: DeviceStateListComplete\r\n");
-	if (!ast_strlen_zero(action_id)) {
-		astman_append(s, "ActionID: %s\r\n", action_id);
-	}
-	astman_append(s, "EventList: Complete\r\n"
-		"ListItems: %d\r\n\r\n", count);
+	astman_send_list_complete_start(s, m, "DeviceStateListComplete", count);
+	astman_send_list_complete_end(s);
 
 	return 0;
 }
diff --git a/res/res_manager_presencestate.c b/res/res_manager_presencestate.c
index e2cfca5..bb9e63a 100644
--- a/res/res_manager_presencestate.c
+++ b/res/res_manager_presencestate.c
@@ -106,12 +106,8 @@ static int action_presencestatelist(struct mansession *s, const struct message *
 	}
 	ao2_iterator_destroy(&it_states);
 
-	astman_append(s, "Event: PresenceStateListComplete\r\n");
-	if (!ast_strlen_zero(action_id)) {
-		astman_append(s, "ActionID: %s\r\n", action_id);
-	}
-	astman_append(s, "EventList: Complete\r\n"
-		"ListItems: %d\r\n\r\n", count);
+	astman_send_list_complete_start(s, m, "PresenceStateListComplete", count);
+	astman_send_list_complete_end(s);
 
 	return 0;
 }
diff --git a/res/res_monitor.c b/res/res_monitor.c
index 98c17f9..ebf9843 100644
--- a/res/res_monitor.c
+++ b/res/res_monitor.c
@@ -24,12 +24,13 @@
  */
 
 /*** MODULEINFO
+	<depend type="module">func_periodic_hook</depend>
 	<support_level>core</support_level>
  ***/
  
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 429033 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/stat.h>
 #include <libgen.h>
diff --git a/res/res_musiconhold.c b/res/res_musiconhold.c
index d0c1b2d..2ac5081 100644
--- a/res/res_musiconhold.c
+++ b/res/res_musiconhold.c
@@ -39,7 +39,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 422037 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <ctype.h>
 #include <signal.h>
@@ -881,7 +881,7 @@ static struct mohclass *_get_mohbyname(const char *name, int warn, int flags, co
 #endif
 
 	if (!moh && warn) {
-		ast_debug(1, "Music on Hold class '%s' not found in memory\n", name);
+		ast_log(LOG_WARNING, "Music on Hold class '%s' not found in memory. Verify your configuration.\n", name);
 	}
 
 	return moh;
diff --git a/res/res_mutestream.c b/res/res_mutestream.c
index 3327b54..479b3d7 100644
--- a/res/res_mutestream.c
+++ b/res/res_mutestream.c
@@ -35,7 +35,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 411328 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/options.h"
 #include "asterisk/logger.h"
diff --git a/res/res_mwi_external.c b/res/res_mwi_external.c
index 820a53f..e5d8a3d 100644
--- a/res/res_mwi_external.c
+++ b/res/res_mwi_external.c
@@ -55,7 +55,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/app.h"
 #include "asterisk/module.h"
diff --git a/res/res_mwi_external_ami.c b/res/res_mwi_external_ami.c
index ea6604f..0f86173 100644
--- a/res/res_mwi_external_ami.c
+++ b/res/res_mwi_external_ami.c
@@ -127,7 +127,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/res_mwi_external.h"
@@ -226,12 +226,8 @@ static int mwi_mailbox_get(struct mansession *s, const struct message *m)
 	ao2_iterator_destroy(&iter);
 	ao2_ref(mailboxes, -1);
 
-	astman_append(s,
-		"Event: MWIGetComplete\r\n"
-		"EventList: Complete\r\n"
-		"ListItems: %u\r\n"
-		"%s"
-		"\r\n", count, id_text);
+	astman_send_list_complete_start(s, m, "MWIGetComplete", count);
+	astman_send_list_complete_end(s);
 
 	return 0;
 }
@@ -361,9 +357,9 @@ static int load_module(void)
 	ast_mwi_external_ref();
 
 	res = 0;
-	res |= ast_manager_register_xml_core("MWIGet", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, mwi_mailbox_get);
-	res |= ast_manager_register_xml_core("MWIDelete", EVENT_FLAG_CALL, mwi_mailbox_delete);
-	res |= ast_manager_register_xml_core("MWIUpdate", EVENT_FLAG_CALL, mwi_mailbox_update);
+	res |= ast_manager_register_xml("MWIGet", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, mwi_mailbox_get);
+	res |= ast_manager_register_xml("MWIDelete", EVENT_FLAG_CALL, mwi_mailbox_delete);
+	res |= ast_manager_register_xml("MWIUpdate", EVENT_FLAG_CALL, mwi_mailbox_update);
 	if (res) {
 		unload_module();
 		return AST_MODULE_LOAD_DECLINE;
diff --git a/res/res_odbc.c b/res/res_odbc.c
index d539f79..171b858 100644
--- a/res/res_odbc.c
+++ b/res/res_odbc.c
@@ -47,7 +47,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/file.h"
 #include "asterisk/channel.h"
@@ -461,7 +461,7 @@ struct odbc_cache_tables *ast_odbc_find_table(const char *database, const char *
 	SQLLEN sqlptr;
 	SQLHSTMT stmt = NULL;
 	int res = 0, error = 0, try = 0;
-	struct odbc_obj *obj = ast_odbc_request_obj(database, 0);
+	struct odbc_obj *obj;
 
 	AST_RWLIST_RDLOCK(&odbc_tables);
 	AST_RWLIST_TRAVERSE(&odbc_tables, tableptr, list) {
@@ -472,13 +472,10 @@ struct odbc_cache_tables *ast_odbc_find_table(const char *database, const char *
 	if (tableptr) {
 		AST_RWLIST_RDLOCK(&tableptr->columns);
 		AST_RWLIST_UNLOCK(&odbc_tables);
-		if (obj) {
-			ast_odbc_release_obj(obj);
-		}
 		return tableptr;
 	}
 
-	if (!obj) {
+	if (!(obj = ast_odbc_request_obj(database, 0))) {
 		ast_log(LOG_WARNING, "Unable to retrieve database handle for table description '%s@%s'\n", tablename, database);
 		AST_RWLIST_UNLOCK(&odbc_tables);
 		return NULL;
@@ -564,9 +561,7 @@ struct odbc_cache_tables *ast_odbc_find_table(const char *database, const char *
 		destroy_table_cache(tableptr);
 		tableptr = NULL;
 	}
-	if (obj) {
-		ast_odbc_release_obj(obj);
-	}
+	ast_odbc_release_obj(obj);
 	return tableptr;
 }
 
@@ -1258,8 +1253,7 @@ struct odbc_obj *_ast_odbc_request_obj2(const char *name, struct ast_flags flags
 		if (obj) {
 			ast_assert(ao2_ref(obj, 0) > 1);
 		}
-		if (!obj && (ast_atomic_fetchadd_int(&class->count, +1) < class->limit) &&
-				(time(NULL) > class->last_negative_connect.tv_sec + class->negative_connection_cache.tv_sec)) {
+		if (!obj && (ast_atomic_fetchadd_int(&class->count, +1) < class->limit)) {
 			obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor);
 			if (!obj) {
 				class->count--;
@@ -1417,10 +1411,7 @@ struct odbc_obj *_ast_odbc_request_obj2(const char *name, struct ast_flags flags
 	}
 
 	if (ast_test_flag(&flags, RES_ODBC_CONNECTED) && !obj->up) {
-		/* Check if this connection qualifies for reconnection, with negative connection cache time */
-		if (time(NULL) > obj->parent->last_negative_connect.tv_sec + obj->parent->negative_connection_cache.tv_sec) {
-			odbc_obj_connect(obj);
-		}
+		odbc_obj_connect(obj);
 	} else if (ast_test_flag(&flags, RES_ODBC_SANITY_CHECK)) {
 		ast_odbc_sanity_check(obj);
 	} else if (obj->parent->idlecheck > 0 && ast_tvdiff_sec(ast_tvnow(), obj->last_used) > obj->parent->idlecheck) {
@@ -1527,6 +1518,7 @@ static odbc_status odbc_obj_connect(struct odbc_obj *obj)
 	char *tracefile = "/tmp/odbc.trace";
 #endif
 	SQLHDBC con;
+	long int negative_cache_expiration;
 
 	if (obj->up) {
 		odbc_obj_disconnect(obj);
@@ -1536,6 +1528,13 @@ static odbc_status odbc_obj_connect(struct odbc_obj *obj)
 		ast_log(LOG_NOTICE, "Connecting %s\n", obj->parent->name);
 	}
 
+	/* Dont connect while server is marked as unreachable via negative_connection_cache */
+	negative_cache_expiration = obj->parent->last_negative_connect.tv_sec + obj->parent->negative_connection_cache.tv_sec;
+	if (time(NULL) < negative_cache_expiration) {
+		ast_log(LOG_WARNING, "Not connecting to %s. Negative connection cache for %ld seconds\n", obj->parent->name, negative_cache_expiration - time(NULL));
+		return ODBC_FAIL;
+	}
+
 	res = SQLAllocHandle(SQL_HANDLE_DBC, obj->parent->env, &con);
 
 	if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
diff --git a/res/res_parking.c b/res/res_parking.c
index cbe880d..3edbd46 100644
--- a/res/res_parking.c
+++ b/res/res_parking.c
@@ -188,7 +188,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "parking/res_parking.h"
 #include "asterisk/config.h"
diff --git a/res/res_phoneprov.c b/res/res_phoneprov.c
index b9c3dbd..6986d20 100644
--- a/res/res_phoneprov.c
+++ b/res/res_phoneprov.c
@@ -52,7 +52,7 @@
 #ifdef SOLARIS
 #include <sys/sockio.h>
 #endif
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 426176 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/channel.h"
 #include "asterisk/file.h"
@@ -1581,12 +1581,20 @@ static int extension_delete_cb(void *obj, void *arg, void *data, int flags)
 
 void ast_phoneprov_delete_extension(char *provider_name, char *macaddress)
 {
+	if (!users) {
+		return;
+	}
+
 	ao2_callback_data(users, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE | OBJ_SEARCH_KEY,
 		extension_delete_cb, macaddress, provider_name);
 }
 
 void ast_phoneprov_delete_extensions(char *provider_name)
 {
+	if (!users) {
+		return;
+	}
+
 	ao2_callback(users, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, extensions_delete_cb, provider_name);
 }
 
diff --git a/res/res_pjsip.c b/res/res_pjsip.c
index f597f27..8e99c55 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -21,6 +21,8 @@
 #include <pjsip.h>
 /* Needed for SUBSCRIBE, NOTIFY, and PUBLISH method definitions */
 #include <pjsip_simple.h>
+#include <pjsip/sip_transaction.h>
+#include <pj/timer.h>
 #include <pjlib.h>
 
 #include "asterisk/res_pjsip.h"
@@ -35,10 +37,17 @@
 #include "asterisk/taskprocessor.h"
 #include "asterisk/uuid.h"
 #include "asterisk/sorcery.h"
+#include "asterisk/file.h"
+#include "asterisk/cli.h"
+#include "asterisk/res_pjsip_cli.h"
+#include "asterisk/test.h"
+#include "asterisk/res_pjsip_presence_xml.h"
 
 /*** MODULEINFO
 	<depend>pjproject</depend>
 	<depend>res_sorcery_config</depend>
+	<depend>res_sorcery_memory</depend>
+	<depend>res_sorcery_astdb</depend>
 	<support_level>core</support_level>
  ***/
 
@@ -195,7 +204,7 @@
 						<para>This setting allows to choose the DTMF mode for endpoint communication.</para>
 						<enumlist>
 							<enum name="rfc4733">
-								<para>DTMF is sent out of band of the main audio stream.This
+								<para>DTMF is sent out of band of the main audio stream.  This
 								supercedes the older <emphasis>RFC-2833</emphasis> used within
 								the older <literal>chan_sip</literal>.</para>
 							</enum>
@@ -205,6 +214,10 @@
 							<enum name="info">
 								<para>DTMF is sent as SIP INFO packets.</para>
 							</enum>
+                                                        <enum name="auto">
+                                                                <para>DTMF is sent as RFC 4733 if the other side supports it or as INBAND if not.</para>
+                                                        </enum>
+
 						</enumlist>
 					</description>
 				</configOption>
@@ -291,9 +304,9 @@
 				<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 will be changed to have the
-						source IP address and port. This option does not affect outbound messages send 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.
 					</para></description>
 				</configOption>
 				<configOption name="rtp_ipv6" default="no">
@@ -312,6 +325,27 @@
 				<configOption name="send_rpid" default="no">
 					<synopsis>Send the Remote-Party-ID header</synopsis>
 				</configOption>
+				<configOption name="rpid_immediate" default="no">
+					<synopsis>Immediately send connected line updates on unanswered incoming calls.</synopsis>
+					<description>
+						<para>When enabled, immediately send <emphasis>180 Ringing</emphasis>
+						or <emphasis>183 Progress</emphasis> response messages to the
+						caller if the connected line information is updated before
+						the call is answered.  This can send a <emphasis>180 Ringing</emphasis>
+						response before the call has even reached the far end.  The
+						caller can start hearing ringback before the far end even gets
+						the call.  Many phones tend to grab the first connected line
+						information and refuse to update the display if it changes.  The
+						first information is not likely to be correct if the call
+						goes to an endpoint not under the control of this Asterisk
+						box.</para>
+						<para>When disabled, a connected line update must wait for
+						another reason to send a message with the connected line
+						information to the caller before the call is answered.  You can
+						trigger the sending of the information by using an appropriate
+						dialplan application such as <emphasis>Ringing</emphasis>.</para>
+					</description>
+				</configOption>
 				<configOption name="timers_min_se" default="90">
 					<synopsis>Minimum session timers expiration period</synopsis>
 					<description><para>
@@ -322,10 +356,11 @@
 					<synopsis>Session timers for SIP packets</synopsis>
 					<description>
 						<enumlist>
-							<enum name="forced" />
 							<enum name="no" />
-							<enum name="required" />
 							<enum name="yes" />
+							<enum name="required" />
+							<enum name="always" />
+							<enum name="forced"><para>Alias of always</para></enum>
 						</enumlist>
 					</description>
 				</configOption>
@@ -438,6 +473,15 @@
 						set to <literal>sdes</literal> or <literal>dtls</literal>.
 					</para></description>
 				</configOption>
+				<configOption name="g726_non_standard" default="no">
+					<synopsis>Force g.726 to use AAL2 packing order when negotiating g.726 audio</synopsis>
+					<description><para>
+                                                When set to "yes" and an endpoint negotiates g.726 audio then use g.726 for AAL2
+                                                packing order instead of what is recommended by RFC3551. Since this essentially
+                                                replaces the underlying 'g726' codec with 'g726aal2' then 'g726aal2' needs to be
+                                                specified in the endpoint's allowed codec list.
+					</para></description>
+				</configOption>
 				<configOption name="inband_progress" default="no">
 					<synopsis>Determines whether chan_pjsip will indicate ringing using inband
 					    progress.</synopsis>
@@ -580,6 +624,9 @@
 				<configOption name="allow_transfer" default="yes">
 					<synopsis>Determines whether SIP REFER transfers are allowed for this endpoint</synopsis>
 				</configOption>
+				<configOption name="user_eq_phone" default="no">
+					<synopsis>Determines whether a user=phone parameter is placed into the request URI if the user is determined to be a phone number</synopsis>
+				</configOption>
 				<configOption name="sdp_owner" default="-">
 					<synopsis>String placed as the username portion of an SDP origin (o=) line.</synopsis>
 				</configOption>
@@ -742,6 +789,30 @@
 						have this accountcode set on it.
 					</para></description>
 				</configOption>
+				<configOption name="rtp_keepalive">
+					<synopsis>Number of seconds between RTP comfort noise keepalive packets.</synopsis>
+					<description><para>
+						At the specified interval, Asterisk will send an RTP comfort noise frame. This may
+						be useful for situations where Asterisk is behind a NAT or firewall and must keep a
+						hole open in order to allow for media to arrive at Asterisk.
+					</para></description>
+				</configOption>
+				<configOption name="rtp_timeout" default="0">
+					<synopsis>Maximum number of seconds without receiving RTP (while off hold) before terminating call.</synopsis>
+					<description><para>
+						This option configures the number of seconds without RTP (while off hold) before
+						considering a channel as dead. When the number of seconds is reached the underlying
+						channel is hung up. By default this option is set to 0, which means do not check.
+					</para></description>
+				</configOption>
+				<configOption name="rtp_timeout_hold" default="0">
+					<synopsis>Maximum number of seconds without receiving RTP (while on hold) before terminating call.</synopsis>
+					<description><para>
+						This option configures the number of seconds without RTP (while on hold) before
+						considering a channel as dead. When the number of seconds is reached the underlying
+						channel is hung up. By default this option is set to 0, which means do not check.
+					</para></description>
+				</configOption>
 			</configObject>
 			<configObject name="auth">
 				<synopsis>Authentication type</synopsis>
@@ -823,6 +894,9 @@
 				<configOption name="ca_list_file">
 					<synopsis>File containing a list of certificates to read (TLS ONLY)</synopsis>
 				</configOption>
+				<configOption name="ca_list_path">
+					<synopsis>Path to directory containing a list of certificates to read (TLS ONLY)</synopsis>
+				</configOption>
 				<configOption name="cert_file">
 					<synopsis>Certificate file for endpoint (TLS ONLY)</synopsis>
 					<description><para>
@@ -868,8 +942,12 @@
 					<synopsis>Method of SSL transport (TLS ONLY)</synopsis>
 					<description>
 						<enumlist>
-							<enum name="default" />
-							<enum name="unspecified" />
+							<enum name="default">
+								<para>The default as defined by PJSIP. This is currently TLSv1, but may change with future releases.</para>
+							</enum>
+							<enum name="unspecified">
+								<para>This option is equivalent to setting 'default'</para>
+							</enum>
 							<enum name="tlsv1" />
 							<enum name="sslv2" />
 							<enum name="sslv3" />
@@ -965,6 +1043,14 @@
 						If <literal>0</literal> never qualify. Time in seconds.
 					</para></description>
 				</configOption>
+				<configOption name="qualify_timeout" default="3.0">
+					<synopsis>Timeout for qualify</synopsis>
+					<description><para>
+						If the contact doesn't repond to the OPTIONS request before the timeout,
+						the contact is marked unavailable.
+						If <literal>0</literal> no timeout. Time in fractional seconds.
+					</para></description>
+				</configOption>
 				<configOption name="outbound_proxy">
 					<synopsis>Outbound proxy used when sending OPTIONS request</synopsis>
 					<description><para>
@@ -1079,6 +1165,14 @@
 						If <literal>0</literal> never qualify. Time in seconds.
 					</para></description>
 				</configOption>
+				<configOption name="qualify_timeout" default="3.0">
+					<synopsis>Timeout for qualify</synopsis>
+					<description><para>
+						If the contact doesn't repond to the OPTIONS request before the timeout,
+						the contact is marked unavailable.
+						If <literal>0</literal> no timeout. Time in fractional seconds.
+					</para></description>
+				</configOption>
 				<configOption name="authenticate_qualify" default="no">
 					<synopsis>Authenticates a qualify request if needed</synopsis>
 					<description><para>
@@ -1164,6 +1258,13 @@
 				<configOption name="max_forwards" default="70">
 					<synopsis>Value used in Max-Forwards header for SIP requests.</synopsis>
 				</configOption>
+				<configOption name="keep_alive_interval" default="0">
+					<synopsis>The interval (in seconds) to send keepalives to active connection-oriented transports.</synopsis>
+				</configOption>
+				<configOption name="max_initial_qualify_time" default="0">
+					<synopsis>The maximum amount of time from startup that qualifies should be attempted on all contacts.
+					If greater than the qualify_frequency for an aor, qualify_frequency will be used instead.</synopsis>
+				</configOption>
 				<configOption name="type">
 					<synopsis>Must be of type 'global'.</synopsis>
 				</configOption>
@@ -1177,6 +1278,16 @@
 					<synopsis>Enable/Disable SIP debug logging.  Valid options include yes|no or
                                         a host address</synopsis>
 				</configOption>
+				<configOption name="endpoint_identifier_order" default="ip,username,anonymous">
+					<synopsis>The order by which endpoint identifiers are processed and checked.
+                                        Identifier names are usually derived from and can be found in the endpoint
+                                        identifier module itself (res_pjsip_endpoint_identifier_*)</synopsis>
+				</configOption>
+				<configOption name="default_from_user" default="asterisk">
+					<synopsis>When Asterisk generates an outgoing SIP request, the From header username will be
+                                        set to this value if there is no better option (such as CallerID) to be
+                                        used.</synopsis>
+				</configOption>
 			</configObject>
 		</configFile>
 	</configInfo>
@@ -1324,6 +1435,9 @@
 				<parameter name="CaListFile">
 					<para><xi:include xpointer="xpointer(/docs/configInfo[@name='res_pjsip']/configFile[@name='pjsip.conf']/configObject[@name='transport']/configOption[@name='ca_list_file']/synopsis/node())"/></para>
 				</parameter>
+				<parameter name="CaListPath">
+					<para><xi:include xpointer="xpointer(/docs/configInfo[@name='res_pjsip']/configFile[@name='pjsip.conf']/configObject[@name='transport']/configOption[@name='ca_list_path']/synopsis/node())"/></para>
+				</parameter>
 				<parameter name="CertFile">
 					<para><xi:include xpointer="xpointer(/docs/configInfo[@name='res_pjsip']/configFile[@name='pjsip.conf']/configObject[@name='transport']/configOption[@name='cert_file']/synopsis/node())"/></para>
 				</parameter>
@@ -1568,6 +1682,9 @@
 				<parameter name="AllowTransfer">
 					<para><xi:include xpointer="xpointer(/docs/configInfo[@name='res_pjsip']/configFile[@name='pjsip.conf']/configObject[@name='endpoint']/configOption[@name='allow_transfer']/synopsis/node())"/></para>
 				</parameter>
+				<parameter name="UserEqPhone">
+					<para><xi:include xpointer="xpointer(/docs/configInfo[@name='res_pjsip']/configFile[@name='pjsip.conf']/configObject[@name='endpoint']/configOption[@name='user_eq_phone']/synopsis/node())"/></para>
+				</parameter>
 				<parameter name="SdpOwner">
 					<para><xi:include xpointer="xpointer(/docs/configInfo[@name='res_pjsip']/configFile[@name='pjsip.conf']/configObject[@name='endpoint']/configOption[@name='sdp_owner']/synopsis/node())"/></para>
 				</parameter>
@@ -1781,11 +1898,32 @@
 
 #define MOD_DATA_CONTACT "contact"
 
+/*! Number of serializers in pool if one not supplied. */
+#define SERIALIZER_POOL_SIZE		8
+
+/*! Next serializer pool index to use. */
+static int serializer_pool_pos;
+
+/*! Pool of serializers to use if not supplied. */
+static struct ast_taskprocessor *serializer_pool[SERIALIZER_POOL_SIZE];
+
 static pjsip_endpoint *ast_pjsip_endpoint;
 
 static struct ast_threadpool *sip_threadpool;
 
-static int register_service(void *data)
+/*! Local host address for IPv4 */
+static pj_sockaddr host_ip_ipv4;
+
+/*! Local host address for IPv4 (string form) */
+static char host_ip_ipv4_string[PJ_INET6_ADDRSTRLEN + 2];
+
+/*! Local host address for IPv6 */
+static pj_sockaddr host_ip_ipv6;
+
+/*! Local host address for IPv6 (string form) */
+static char host_ip_ipv6_string[PJ_INET6_ADDRSTRLEN + 2];
+
+static int register_service_noref(void *data)
 {
 	pjsip_module **module = data;
 	if (!ast_pjsip_endpoint) {
@@ -1797,19 +1935,33 @@ static int register_service(void *data)
 		return -1;
 	}
 	ast_debug(1, "Registered SIP service %.*s (%p)\n", (int) pj_strlen(&(*module)->name), pj_strbuf(&(*module)->name), *module);
-	ast_module_ref(ast_module_info->self);
 	return 0;
 }
 
+static int register_service(void *data)
+{
+	int res;
+
+	if (!(res = register_service_noref(data))) {
+		ast_module_ref(ast_module_info->self);
+	}
+
+	return res;
+}
+
+int internal_sip_register_service(pjsip_module *module)
+{
+	return ast_sip_push_task_synchronous(NULL, register_service_noref, &module);
+}
+
 int ast_sip_register_service(pjsip_module *module)
 {
 	return ast_sip_push_task_synchronous(NULL, register_service, &module);
 }
 
-static int unregister_service(void *data)
+static int unregister_service_noref(void *data)
 {
 	pjsip_module **module = data;
-	ast_module_unref(ast_module_info->self);
 	if (!ast_pjsip_endpoint) {
 		return -1;
 	}
@@ -1818,6 +1970,22 @@ static int unregister_service(void *data)
 	return 0;
 }
 
+static int unregister_service(void *data)
+{
+	int res;
+
+	if (!(res = unregister_service_noref(data))) {
+		ast_module_unref(ast_module_info->self);
+	}
+
+	return res;
+}
+
+int internal_sip_unregister_service(pjsip_module *module)
+{
+	return ast_sip_push_task_synchronous(NULL, unregister_service_noref, &module);
+}
+
 void ast_sip_unregister_service(pjsip_module *module)
 {
 	ast_sip_push_task_synchronous(NULL, unregister_service, &module);
@@ -1905,16 +2073,30 @@ int ast_sip_create_request_with_auth(const struct ast_sip_auth_vector *auths, pj
 	return registered_outbound_authenticator->create_request_with_auth(auths, challenge, tsx, new_request);
 }
 
+int ast_sip_create_request_with_auth_from_old(const struct ast_sip_auth_vector *auths, pjsip_rx_data *challenge,
+		pjsip_tx_data *old_request, pjsip_tx_data **new_request)
+{
+	if (!registered_outbound_authenticator) {
+		ast_log(LOG_WARNING, "No SIP outbound authenticator registered. Cannot respond to authentication challenge\n");
+		return -1;
+	}
+	return registered_outbound_authenticator->create_request_with_auth_from_old(auths, challenge, old_request, new_request);
+}
+
 struct endpoint_identifier_list {
+	const char *name;
+	unsigned int priority;
 	struct ast_sip_endpoint_identifier *identifier;
 	AST_RWLIST_ENTRY(endpoint_identifier_list) list;
 };
 
 static AST_RWLIST_HEAD_STATIC(endpoint_identifiers, endpoint_identifier_list);
 
-int ast_sip_register_endpoint_identifier(struct ast_sip_endpoint_identifier *identifier)
+int ast_sip_register_endpoint_identifier_with_name(struct ast_sip_endpoint_identifier *identifier,
+						 const char *name)
 {
-	struct endpoint_identifier_list *id_list_item;
+	char *prev, *current, *identifier_order;
+	struct endpoint_identifier_list *iter, *id_list_item;
 	SCOPED_LOCK(lock, &endpoint_identifiers, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK);
 
 	id_list_item = ast_calloc(1, sizeof(*id_list_item));
@@ -1923,14 +2105,78 @@ int ast_sip_register_endpoint_identifier(struct ast_sip_endpoint_identifier *ide
 		return -1;
 	}
 	id_list_item->identifier = identifier;
+	id_list_item->name = name;
+
+	ast_debug(1, "Register endpoint identifier %s (%p)\n", name, identifier);
+
+	if (ast_strlen_zero(name)) {
+		/* if an identifier has no name then place in front */
+		AST_RWLIST_INSERT_HEAD(&endpoint_identifiers, id_list_item, list);
+		ast_module_ref(ast_module_info->self);
+		return 0;
+	}
+
+	/* see if the name of the identifier is in the global endpoint_identifier_order list */
+	identifier_order = prev = current = ast_sip_get_endpoint_identifier_order();
+
+	if (ast_strlen_zero(identifier_order)) {
+		id_list_item->priority = UINT_MAX;
+		AST_RWLIST_INSERT_TAIL(&endpoint_identifiers, id_list_item, list);
+		ast_module_ref(ast_module_info->self);
+		ast_free(identifier_order);
+		return 0;
+	}
+
+	id_list_item->priority = 0;
+	while ((current = strchr(current, ','))) {
+		++id_list_item->priority;
+		if (!strncmp(prev, name, current - prev)) {
+			break;
+		}
+		prev = ++current;
+	}
+
+	if (!current) {
+		/* check to see if it is the only or last item */
+		if (!strcmp(prev, name)) {
+			++id_list_item->priority;
+		} else {
+			id_list_item->priority = UINT_MAX;
+		}
+	}
 
-	AST_RWLIST_INSERT_TAIL(&endpoint_identifiers, id_list_item, list);
-	ast_debug(1, "Registered endpoint identifier %p\n", identifier);
+	if (id_list_item->priority == UINT_MAX || AST_RWLIST_EMPTY(&endpoint_identifiers)) {
+		/* if not in the endpoint_identifier_order list then consider it less in
+		   priority and add it to the end */
+		AST_RWLIST_INSERT_TAIL(&endpoint_identifiers, id_list_item, list);
+		ast_module_ref(ast_module_info->self);
+		ast_free(identifier_order);
+		return 0;
+	}
+
+	AST_RWLIST_TRAVERSE_SAFE_BEGIN(&endpoint_identifiers, iter, list) {
+		if (id_list_item->priority < iter->priority) {
+			AST_RWLIST_INSERT_BEFORE_CURRENT(id_list_item, list);
+			break;
+		}
+
+		if (!AST_RWLIST_NEXT(iter, list)) {
+			AST_RWLIST_INSERT_AFTER(&endpoint_identifiers, iter, id_list_item, list);
+			break;
+		}
+	}
+	AST_RWLIST_TRAVERSE_SAFE_END;
 
 	ast_module_ref(ast_module_info->self);
+	ast_free(identifier_order);
 	return 0;
 }
 
+int ast_sip_register_endpoint_identifier(struct ast_sip_endpoint_identifier *identifier)
+{
+	return ast_sip_register_endpoint_identifier_with_name(identifier, NULL);
+}
+
 void ast_sip_unregister_endpoint_identifier(struct ast_sip_endpoint_identifier *identifier)
 {
 	struct endpoint_identifier_list *iter;
@@ -1962,28 +2208,107 @@ struct ast_sip_endpoint *ast_sip_identify_endpoint(pjsip_rx_data *rdata)
 	return endpoint;
 }
 
+static char *cli_show_endpoint_identifiers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define ENDPOINT_IDENTIFIER_FORMAT "%-20.20s\n"
+	struct endpoint_identifier_list *iter;
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "pjsip show identifiers";
+		e->usage = "Usage: pjsip show identifiers\n"
+		            "      List all registered endpoint identifiers\n";
+		return NULL;
+	case CLI_GENERATE:
+		return NULL;
+	}
+
+	if (a->argc != 3) {
+                return CLI_SHOWUSAGE;
+        }
+
+	ast_cli(a->fd, ENDPOINT_IDENTIFIER_FORMAT, "Identifier Names:");
+	{
+		SCOPED_LOCK(lock, &endpoint_identifiers, AST_RWLIST_RDLOCK, AST_RWLIST_UNLOCK);
+		AST_RWLIST_TRAVERSE(&endpoint_identifiers, iter, list) {
+			ast_cli(a->fd, ENDPOINT_IDENTIFIER_FORMAT,
+				iter->name ? iter->name : "name not specified");
+		}
+	}
+	return CLI_SUCCESS;
+#undef ENDPOINT_IDENTIFIER_FORMAT
+}
+
+static char *cli_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	struct ast_sip_cli_context context;
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "pjsip show settings";
+		e->usage = "Usage: pjsip show settings\n"
+		            "      Show global and system configuration options\n";
+		return NULL;
+	case CLI_GENERATE:
+		return NULL;
+	}
+
+	context.output_buffer = ast_str_create(256);
+	if (!context.output_buffer) {
+		ast_cli(a->fd, "Could not allocate output buffer.\n");
+		return CLI_FAILURE;
+	}
+
+	if (sip_cli_print_global(&context) || sip_cli_print_system(&context)) {
+		ast_free(context.output_buffer);
+		ast_cli(a->fd, "Error retrieving settings.\n");
+		return CLI_FAILURE;
+	}
+
+	ast_cli(a->fd, "%s", ast_str_buffer(context.output_buffer));
+	ast_free(context.output_buffer);
+	return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_commands[] = {
+        AST_CLI_DEFINE(cli_show_settings, "Show global and system configuration options"),
+        AST_CLI_DEFINE(cli_show_endpoint_identifiers, "List registered endpoint identifiers")
+};
+
 AST_RWLIST_HEAD_STATIC(endpoint_formatters, ast_sip_endpoint_formatter);
 
-int ast_sip_register_endpoint_formatter(struct ast_sip_endpoint_formatter *obj)
+void internal_sip_register_endpoint_formatter(struct ast_sip_endpoint_formatter *obj)
 {
 	SCOPED_LOCK(lock, &endpoint_formatters, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK);
 	AST_RWLIST_INSERT_TAIL(&endpoint_formatters, obj, next);
+}
+
+int ast_sip_register_endpoint_formatter(struct ast_sip_endpoint_formatter *obj)
+{
+	internal_sip_register_endpoint_formatter(obj);
 	ast_module_ref(ast_module_info->self);
 	return 0;
 }
 
-void ast_sip_unregister_endpoint_formatter(struct ast_sip_endpoint_formatter *obj)
+int internal_sip_unregister_endpoint_formatter(struct ast_sip_endpoint_formatter *obj)
 {
 	struct ast_sip_endpoint_formatter *i;
 	SCOPED_LOCK(lock, &endpoint_formatters, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK);
 	AST_RWLIST_TRAVERSE_SAFE_BEGIN(&endpoint_formatters, i, next) {
 		if (i == obj) {
 			AST_RWLIST_REMOVE_CURRENT(next);
-			ast_module_unref(ast_module_info->self);
 			break;
 		}
 	}
 	AST_RWLIST_TRAVERSE_SAFE_END;
+	return i == obj ? 0 : -1;
+}
+
+void ast_sip_unregister_endpoint_formatter(struct ast_sip_endpoint_formatter *obj)
+{
+	if (!internal_sip_unregister_endpoint_formatter(obj)) {
+		ast_module_unref(ast_module_info->self);
+	}
 }
 
 int ast_sip_format_endpoint_ami(struct ast_sip_endpoint *endpoint,
@@ -2017,10 +2342,11 @@ static int sip_dialog_create_from(pj_pool_t *pool, pj_str_t *from, const char *u
 	pjsip_sip_uri *sip_uri;
 	pjsip_transport_type_e type = PJSIP_TRANSPORT_UNSPECIFIED;
 	int local_port;
-	char uuid_str[AST_UUID_STR_LEN];
+	char default_user[PJSIP_MAX_URL_SIZE];
 
 	if (ast_strlen_zero(user)) {
-		user = ast_uuid_generate_str(uuid_str, sizeof(uuid_str));
+		ast_sip_get_default_from_user(default_user, sizeof(default_user));
+		user = default_user;
 	}
 
 	/* Parse the provided target URI so we can determine what transport it will end up using */
@@ -2127,10 +2453,50 @@ static int sip_get_tpselector_from_endpoint(const struct ast_sip_endpoint *endpo
 	return 0;
 }
 
+void ast_sip_add_usereqphone(const struct ast_sip_endpoint *endpoint, pj_pool_t *pool, pjsip_uri *uri)
+{
+	pjsip_sip_uri *sip_uri;
+	int i = 0;
+	pjsip_param *param;
+	const pj_str_t STR_USER = { "user", 4 };
+	const pj_str_t STR_PHONE = { "phone", 5 };
+
+	if (!endpoint || !endpoint->usereqphone || (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri))) {
+		return;
+	}
+
+	sip_uri = pjsip_uri_get_uri(uri);
+
+	if (!pj_strlen(&sip_uri->user)) {
+		return;
+	}
+
+	if (pj_strbuf(&sip_uri->user)[0] == '+') {
+		i = 1;
+	}
+
+	/* Test URI user against allowed characters in AST_DIGIT_ANY */
+	for (; i < pj_strlen(&sip_uri->user); i++) {
+		if (!strchr(AST_DIGIT_ANYNUM, pj_strbuf(&sip_uri->user)[i])) {
+			break;
+		}
+	}
+
+	if (i < pj_strlen(&sip_uri->user)) {
+		return;
+	}
+
+	param = PJ_POOL_ALLOC_T(pool, pjsip_param);
+	param->name = STR_USER;
+	param->value = STR_PHONE;
+	pj_list_insert_before(&sip_uri->other_param, param);
+}
+
 pjsip_dialog *ast_sip_create_dialog_uac(const struct ast_sip_endpoint *endpoint, const char *uri, const char *request_user)
 {
 	char enclosed_uri[PJSIP_MAX_URL_SIZE];
 	pj_str_t local_uri = { "sip:temp at temp", 13 }, remote_uri, target_uri;
+	pj_status_t res;
 	pjsip_dialog *dlg = NULL;
 	const char *outbound_proxy = endpoint->outbound_proxy;
 	pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, };
@@ -2141,7 +2507,12 @@ pjsip_dialog *ast_sip_create_dialog_uac(const struct ast_sip_endpoint *endpoint,
 
 	pj_cstr(&target_uri, uri);
 
-	if (pjsip_dlg_create_uac(pjsip_ua_instance(), &local_uri, NULL, &remote_uri, &target_uri, &dlg) != PJ_SUCCESS) {
+	res = pjsip_dlg_create_uac(pjsip_ua_instance(), &local_uri, NULL, &remote_uri, &target_uri, &dlg);
+	if (res != PJ_SUCCESS) {
+		if (res == PJSIP_EINVALIDURI) {
+			ast_log(LOG_ERROR, "Could not create dialog to endpoint '%s' as URI '%s' is not valid\n",
+				ast_sorcery_object_get_id(endpoint), uri);
+		}
 		return NULL;
 	}
 
@@ -2174,6 +2545,10 @@ pjsip_dialog *ast_sip_create_dialog_uac(const struct ast_sip_endpoint *endpoint,
 		}
 	}
 
+	/* Add the user=phone parameter if applicable */
+	ast_sip_add_usereqphone(endpoint, dlg->pool, dlg->target);
+	ast_sip_add_usereqphone(endpoint, dlg->pool, dlg->remote.info->uri);
+
 	/* We have to temporarily bump up the sess_count here so the dialog is not prematurely destroyed */
 	dlg->sess_count++;
 
@@ -2188,6 +2563,8 @@ pjsip_dialog *ast_sip_create_dialog_uac(const struct ast_sip_endpoint *endpoint,
 
 		pj_strdup2_with_null(dlg->pool, &tmp, outbound_proxy);
 		if (!(route = pjsip_parse_hdr(dlg->pool, &ROUTE_HNAME, tmp.ptr, tmp.slen, NULL))) {
+			ast_log(LOG_ERROR, "Could not create dialog to endpoint '%s' as outbound proxy URI '%s' is not valid\n",
+				ast_sorcery_object_get_id(endpoint), outbound_proxy);
 			dlg->sess_count--;
 			pjsip_dlg_terminate(dlg);
 			return NULL;
@@ -2202,22 +2579,71 @@ pjsip_dialog *ast_sip_create_dialog_uac(const struct ast_sip_endpoint *endpoint,
 	return dlg;
 }
 
+/*!
+ * \brief Determine if a SIPS Contact header is required.
+ *
+ * This uses the guideline provided in RFC 3261 Section 12.1.1 to
+ * determine if the Contact header must be a sips: URI.
+ *
+ * \param rdata The incoming dialog-starting request
+ * \retval 0 SIPS not required
+ * \retval 1 SIPS required
+ */
+static int uas_use_sips_contact(pjsip_rx_data *rdata)
+{
+	pjsip_rr_hdr *record_route;
+
+	if (PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.msg->line.req.uri)) {
+		return 1;
+	}
+
+	record_route = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_RECORD_ROUTE, NULL);
+	if (record_route) {
+		if (PJSIP_URI_SCHEME_IS_SIPS(&record_route->name_addr)) {
+			return 1;
+		}
+	} else {
+		pjsip_contact_hdr *contact;
+
+		contact = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL);
+		ast_assert(contact != NULL);
+		if (PJSIP_URI_SCHEME_IS_SIPS(contact->uri)) {
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
 pjsip_dialog *ast_sip_create_dialog_uas(const struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata, pj_status_t *status)
 {
 	pjsip_dialog *dlg;
 	pj_str_t contact;
 	pjsip_transport_type_e type = rdata->tp_info.transport->key.type;
+	pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, };
+	pjsip_transport *transport;
 
 	ast_assert(status != NULL);
 
+	if (sip_get_tpselector_from_endpoint(endpoint, &selector)) {
+		return NULL;
+	}
+
+	transport = rdata->tp_info.transport;
+	if (selector.type == PJSIP_TPSELECTOR_TRANSPORT) {
+		transport = selector.u.transport;
+	}
+	type = transport->key.type;
+
 	contact.ptr = pj_pool_alloc(rdata->tp_info.pool, PJSIP_MAX_URL_SIZE);
 	contact.slen = pj_ansi_snprintf(contact.ptr, PJSIP_MAX_URL_SIZE,
-			"<sip:%s%.*s%s:%d%s%s>",
+			"<%s:%s%.*s%s:%d%s%s>",
+			uas_use_sips_contact(rdata) ? "sips" : "sip",
 			(type & PJSIP_TRANSPORT_IPV6) ? "[" : "",
-			(int)rdata->tp_info.transport->local_name.host.slen,
-			rdata->tp_info.transport->local_name.host.ptr,
+			(int)transport->local_name.host.slen,
+			transport->local_name.host.ptr,
 			(type & PJSIP_TRANSPORT_IPV6) ? "]" : "",
-			rdata->tp_info.transport->local_name.port,
+			transport->local_name.port,
 			(type != PJSIP_TRANSPORT_UDP && type != PJSIP_TRANSPORT_UDP6) ? ";transport=" : "",
 			(type != PJSIP_TRANSPORT_UDP && type != PJSIP_TRANSPORT_UDP6) ? pjsip_transport_get_type_name(type) : "");
 
@@ -2231,6 +2657,10 @@ pjsip_dialog *ast_sip_create_dialog_uas(const struct ast_sip_endpoint *endpoint,
 		return NULL;
 	}
 
+	dlg->sess_count++;
+	pjsip_dlg_set_transport(dlg, &selector);
+	dlg->sess_count--;
+
 	return dlg;
 }
 
@@ -2239,6 +2669,12 @@ int ast_sip_create_rdata(pjsip_rx_data *rdata, char *packet, const char *src_nam
 {
 	pj_str_t tmp;
 
+	/*
+	 * Initialize the error list in case there is a parse error
+	 * in the given packet.
+	 */
+	pj_list_init(&rdata->msg_info.parse_err);
+
 	rdata->tp_info.transport = PJ_POOL_ZALLOC_T(rdata->tp_info.pool, pjsip_transport);
 	if (!rdata->tp_info.transport) {
 		return -1;
@@ -2249,7 +2685,7 @@ int ast_sip_create_rdata(pjsip_rx_data *rdata, char *packet, const char *src_nam
 	rdata->pkt_info.src_port = src_port;
 
 	pjsip_parse_rdata(packet, strlen(packet), rdata);
-	if (!rdata->msg_info.msg) {
+	if (!rdata->msg_info.msg || !pj_list_empty(&rdata->msg_info.parse_err)) {
 		return -1;
 	}
 
@@ -2322,6 +2758,7 @@ static int create_out_of_dialog_request(const pjsip_method *method, struct ast_s
 	pj_str_t from;
 	pj_pool_t *pool;
 	pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, };
+	pjsip_uri *sip_uri;
 
 	if (ast_strlen_zero(uri)) {
 		if (!endpoint && (!contact || ast_strlen_zero(contact->uri))) {
@@ -2358,10 +2795,21 @@ static int create_out_of_dialog_request(const pjsip_method *method, struct ast_s
 		return -1;
 	}
 
+	sip_uri = pjsip_parse_uri(pool, remote_uri.ptr, remote_uri.slen, 0);
+	if (!sip_uri || (!PJSIP_URI_SCHEME_IS_SIP(sip_uri) && !PJSIP_URI_SCHEME_IS_SIPS(sip_uri))) {
+		ast_log(LOG_ERROR, "Unable to create outbound %.*s request to endpoint %s as URI '%s' is not valid\n",
+			(int) pj_strlen(&method->name), pj_strbuf(&method->name),
+			endpoint ? ast_sorcery_object_get_id(endpoint) : "<none>",
+			pj_strbuf(&remote_uri));
+		pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
+		return -1;
+	}
+
 	if (sip_dialog_create_from(pool, &from, endpoint ? endpoint->fromuser : NULL,
 				endpoint ? endpoint->fromdomain : NULL, &remote_uri, &selector)) {
 		ast_log(LOG_ERROR, "Unable to create From header for %.*s request to endpoint %s\n",
-				(int) pj_strlen(&method->name), pj_strbuf(&method->name), ast_sorcery_object_get_id(endpoint));
+				(int) pj_strlen(&method->name), pj_strbuf(&method->name),
+				endpoint ? ast_sorcery_object_get_id(endpoint) : "<none>");
 		pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
 		return -1;
 	}
@@ -2369,16 +2817,21 @@ static int create_out_of_dialog_request(const pjsip_method *method, struct ast_s
 	if (pjsip_endpt_create_request(ast_sip_get_pjsip_endpoint(), method, &remote_uri,
 			&from, &remote_uri, &from, NULL, -1, NULL, tdata) != PJ_SUCCESS) {
 		ast_log(LOG_ERROR, "Unable to create outbound %.*s request to endpoint %s\n",
-				(int) pj_strlen(&method->name), pj_strbuf(&method->name), ast_sorcery_object_get_id(endpoint));
+				(int) pj_strlen(&method->name), pj_strbuf(&method->name),
+				endpoint ? ast_sorcery_object_get_id(endpoint) : "<none>");
 		pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
 		return -1;
 	}
 
+	/* Add the user=phone parameter if applicable */
+	ast_sip_add_usereqphone(endpoint, (*tdata)->pool, (*tdata)->msg->line.req.uri);
+
 	/* If an outbound proxy is specified on the endpoint apply it to this request */
 	if (endpoint && !ast_strlen_zero(endpoint->outbound_proxy) &&
 		ast_sip_set_outbound_proxy((*tdata), endpoint->outbound_proxy)) {
-		ast_log(LOG_ERROR, "Unable to apply outbound proxy on request %.*s to endpoint %s\n",
-			(int) pj_strlen(&method->name), pj_strbuf(&method->name), ast_sorcery_object_get_id(endpoint));
+		ast_log(LOG_ERROR, "Unable to apply outbound proxy on request %.*s to endpoint %s as outbound proxy URI '%s' is not valid\n",
+			(int) pj_strlen(&method->name), pj_strbuf(&method->name), ast_sorcery_object_get_id(endpoint),
+			endpoint->outbound_proxy);
 		pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
 		return -1;
 	}
@@ -2472,6 +2925,8 @@ static pj_bool_t does_method_match(const pj_str_t *message_method, const char *s
 
 /*! Maximum number of challenges before assuming that we are in a loop */
 #define MAX_RX_CHALLENGES	10
+#define TIMER_INACTIVE		0
+#define TIMEOUT_TIMER2		5
 
 /*! \brief Structure to hold information about an outbound request */
 struct send_request_data {
@@ -2517,41 +2972,196 @@ struct send_request_wrapper {
 	void (*callback)(void *token, pjsip_event *e);
 	/*! Non-zero when the callback is called. */
 	unsigned int cb_called;
+	/*! Timeout timer. */
+	pj_timer_entry *timeout_timer;
+	/*! Original timeout. */
+	pj_int32_t timeout;
+	/*! The transmit data. */
+	pjsip_tx_data *tdata;
 };
 
-static void endpt_send_request_wrapper(void *token, pjsip_event *e)
+/*! \internal This function gets called by pjsip when the transaction ends,
+ * even if it timed out.  The lock prevents a race condition if both the pjsip
+ * transaction timer and our own timer expire simultaneously.
+ */
+static void endpt_send_request_cb(void *token, pjsip_event *e)
 {
 	struct send_request_wrapper *req_wrapper = token;
 
-	req_wrapper->cb_called = 1;
-	if (req_wrapper->callback) {
+	if (e->body.tsx_state.type == PJSIP_EVENT_TIMER) {
+		ast_debug(2, "%p: PJSIP tsx timer expired\n", req_wrapper);
+
+		if (req_wrapper->timeout_timer
+			&& req_wrapper->timeout_timer->id != TIMEOUT_TIMER2) {
+			ast_debug(3, "%p: Timeout already handled\n", req_wrapper);
+			ao2_ref(req_wrapper, -1);
+			return;
+		}
+	} else {
+		ast_debug(2, "%p: PJSIP tsx response received\n", req_wrapper);
+	}
+
+	ao2_lock(req_wrapper);
+
+	/* It's possible that our own timer was already processing while
+	 * we were waiting on the lock so check the timer id.  If it's
+	 * still TIMER2 then we still need to process.
+	 */
+	if (req_wrapper->timeout_timer
+		&& req_wrapper->timeout_timer->id == TIMEOUT_TIMER2) {
+		int timers_cancelled = 0;
+
+		ast_debug(3, "%p: Cancelling timer\n", req_wrapper);
+
+		timers_cancelled = pj_timer_heap_cancel_if_active(
+			pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()),
+			req_wrapper->timeout_timer, TIMER_INACTIVE);
+
+		if (timers_cancelled > 0) {
+			/* If the timer was cancelled the callback will never run so
+			 * clean up its reference to the wrapper.
+			 */
+			ast_debug(3, "%p: Timer cancelled\n", req_wrapper);
+			ao2_ref(req_wrapper, -1);
+		} else {
+			/* If it wasn't cancelled, it MAY be in the callback already
+			 * waiting on the lock so set the id to INACTIVE so
+			 * when the callback comes out of the lock, it knows to not
+			 * proceed.
+			 */
+			ast_debug(3, "%p: Timer already expired\n", req_wrapper);
+			req_wrapper->timeout_timer->id = TIMER_INACTIVE;
+		}
+	}
+
+	/* It's possible that our own timer expired and called the callbacks
+	 * so no need to call them again.
+	 */
+	if (!req_wrapper->cb_called && req_wrapper->callback) {
 		req_wrapper->callback(req_wrapper->token, e);
+		req_wrapper->cb_called = 1;
+		ast_debug(2, "%p: Callbacks executed\n", req_wrapper);
 	}
+	ao2_unlock(req_wrapper);
 	ao2_ref(req_wrapper, -1);
 }
 
+/*! \internal This function gets called by our own timer when it expires.
+ * If the timer is cancelled however, the function does NOT get called.
+ * The lock prevents a race condition if both the pjsip transaction timer
+ * and our own timer expire simultaneously.
+ */
+static void send_request_timer_callback(pj_timer_heap_t *theap, pj_timer_entry *entry)
+{
+	pjsip_event event;
+	struct send_request_wrapper *req_wrapper = entry->user_data;
+
+	ast_debug(2, "%p: Internal tsx timer expired after %d msec\n",
+		req_wrapper, req_wrapper->timeout);
+
+	ao2_lock(req_wrapper);
+	/* If the id is not TIMEOUT_TIMER2 then the timer was cancelled above
+	 * while the lock was being held so just clean up.
+	 */
+	if (entry->id != TIMEOUT_TIMER2) {
+		ao2_unlock(req_wrapper);
+		ast_debug(3, "%p: Timeout already handled\n", req_wrapper);
+		ao2_ref(req_wrapper, -1);
+		return;
+	}
+
+	ast_debug(3, "%p: Timer handled here\n", req_wrapper);
+
+	PJSIP_EVENT_INIT_TX_MSG(event, req_wrapper->tdata);
+	event.body.tsx_state.type = PJSIP_EVENT_TIMER;
+	entry->id = TIMER_INACTIVE;
+
+	if (!req_wrapper->cb_called && req_wrapper->callback) {
+		req_wrapper->callback(req_wrapper->token, &event);
+		req_wrapper->cb_called = 1;
+		ast_debug(2, "%p: Callbacks executed\n", req_wrapper);
+	}
+
+	ao2_unlock(req_wrapper);
+	ao2_ref(req_wrapper, -1);
+}
+
+static void send_request_wrapper_destructor(void *obj)
+{
+	struct send_request_wrapper *req_wrapper = obj;
+
+	pjsip_tx_data_dec_ref(req_wrapper->tdata);
+	ast_debug(2, "%p: wrapper destroyed\n", req_wrapper);
+}
+
 static pj_status_t endpt_send_request(struct ast_sip_endpoint *endpoint,
 	pjsip_tx_data *tdata, pj_int32_t timeout, void *token, pjsip_endpt_send_callback cb)
 {
 	struct send_request_wrapper *req_wrapper;
 	pj_status_t ret_val;
+	pjsip_endpoint *endpt = ast_sip_get_pjsip_endpoint();
 
 	/* Create wrapper to detect if the callback was actually called on an error. */
-	req_wrapper = ao2_alloc_options(sizeof(*req_wrapper), NULL,
-		AO2_ALLOC_OPT_LOCK_NOLOCK);
+	req_wrapper = ao2_alloc(sizeof(*req_wrapper), send_request_wrapper_destructor);
 	if (!req_wrapper) {
 		pjsip_tx_data_dec_ref(tdata);
 		return PJ_ENOMEM;
 	}
+
+	ast_debug(2, "%p: Wrapper created\n", req_wrapper);
+
 	req_wrapper->token = token;
 	req_wrapper->callback = cb;
+	req_wrapper->timeout = timeout;
+	req_wrapper->timeout_timer = NULL;
+	req_wrapper->tdata = tdata;
+	/* Add a reference to tdata.  The wrapper destructor cleans it up. */
+	pjsip_tx_data_add_ref(tdata);
+
+	ao2_lock(req_wrapper);
+
+	if (timeout > 0) {
+		pj_time_val timeout_timer_val = { timeout / 1000, timeout % 1000 };
+
+		req_wrapper->timeout_timer = PJ_POOL_ALLOC_T(tdata->pool, pj_timer_entry);
+
+		ast_debug(2, "%p: Set timer to %d msec\n", req_wrapper, timeout);
 
+		pj_timer_entry_init(req_wrapper->timeout_timer, TIMEOUT_TIMER2,
+			req_wrapper, &send_request_timer_callback);
+
+		pj_timer_heap_cancel_if_active(pjsip_endpt_get_timer_heap(endpt),
+			req_wrapper->timeout_timer, TIMER_INACTIVE);
+
+		/* We need to insure that the wrapper and tdata are available if/when the
+		 * timer callback is executed.
+		 */
+		ao2_ref(req_wrapper, +1);
+		pj_timer_heap_schedule(pjsip_endpt_get_timer_heap(endpt),
+			req_wrapper->timeout_timer, &timeout_timer_val);
+
+		req_wrapper->timeout_timer->id = TIMEOUT_TIMER2;
+	} else {
+		req_wrapper->timeout_timer = NULL;
+	}
+
+	/* We need to insure that the wrapper and tdata are available when the
+	 * transaction callback is executed.
+	 */
 	ao2_ref(req_wrapper, +1);
-	ret_val = pjsip_endpt_send_request(ast_sip_get_pjsip_endpoint(), tdata, timeout,
-		req_wrapper, endpt_send_request_wrapper);
+
+	ret_val = pjsip_endpt_send_request(endpt, tdata, -1, req_wrapper, endpt_send_request_cb);
 	if (ret_val != PJ_SUCCESS) {
 		char errmsg[PJ_ERR_MSG_SIZE];
 
+		if (timeout > 0) {
+			int timers_cancelled = pj_timer_heap_cancel_if_active(pjsip_endpt_get_timer_heap(endpt),
+				req_wrapper->timeout_timer, TIMER_INACTIVE);
+			if (timers_cancelled > 0) {
+				ao2_ref(req_wrapper, -1);
+			}
+		}
+
 		/* Complain of failure to send the request. */
 		pj_strerror(ret_val, errmsg, sizeof(errmsg));
 		ast_log(LOG_ERROR, "Error %d '%s' sending %.*s request to endpoint %s\n",
@@ -2572,6 +3182,7 @@ static pj_status_t endpt_send_request(struct ast_sip_endpoint *endpoint,
 			ao2_ref(req_wrapper, -1);
 		}
 	}
+	ao2_unlock(req_wrapper);
 	ao2_ref(req_wrapper, -1);
 	return ret_val;
 }
@@ -2627,7 +3238,7 @@ static void send_request_cb(void *token, pjsip_event *e)
 		}
 		break;
 	default:
-		ast_log(LOG_ERROR, "Unexpected PJSIP event %d\n", e->body.tsx_state.type);
+		ast_log(LOG_ERROR, "Unexpected PJSIP event %u\n", e->body.tsx_state.type);
 		break;
 	}
 
@@ -2637,8 +3248,9 @@ static void send_request_cb(void *token, pjsip_event *e)
 	ao2_ref(req_data, -1);
 }
 
-static int send_out_of_dialog_request(pjsip_tx_data *tdata, struct ast_sip_endpoint *endpoint,
-	void *token, void (*callback)(void *token, pjsip_event *e))
+int ast_sip_send_out_of_dialog_request(pjsip_tx_data *tdata,
+	struct ast_sip_endpoint *endpoint, int timeout, void *token,
+	void (*callback)(void *token, pjsip_event *e))
 {
 	struct ast_sip_supplement *supplement;
 	struct send_request_data *req_data;
@@ -2664,7 +3276,7 @@ static int send_out_of_dialog_request(pjsip_tx_data *tdata, struct ast_sip_endpo
 	ast_sip_mod_data_set(tdata->pool, tdata->mod_data, supplement_module.id, MOD_DATA_CONTACT, NULL);
 	ao2_cleanup(contact);
 
-	if (endpt_send_request(endpoint, tdata, -1, req_data, send_request_cb)
+	if (endpt_send_request(endpoint, tdata, timeout, req_data, send_request_cb)
 		!= PJ_SUCCESS) {
 		ao2_cleanup(req_data);
 		return -1;
@@ -2682,7 +3294,7 @@ int ast_sip_send_request(pjsip_tx_data *tdata, struct pjsip_dialog *dlg,
 	if (dlg) {
 		return send_in_dialog_request(tdata, dlg);
 	} else {
-		return send_out_of_dialog_request(tdata, endpoint, token, callback);
+		return ast_sip_send_out_of_dialog_request(tdata, endpoint, -1, token, callback);
 	}
 }
 
@@ -2767,22 +3379,81 @@ int ast_sip_append_body(pjsip_tx_data *tdata, const char *body_text)
 	return 0;
 }
 
-struct ast_taskprocessor *ast_sip_create_serializer(void)
+struct ast_taskprocessor *ast_sip_create_serializer_group(struct ast_serializer_shutdown_group *shutdown_group)
 {
 	struct ast_taskprocessor *serializer;
 	char name[AST_UUID_STR_LEN];
 
 	ast_uuid_generate_str(name, sizeof(name));
 
-	serializer = ast_threadpool_serializer(name, sip_threadpool);
+	serializer = ast_threadpool_serializer_group(name, sip_threadpool, shutdown_group);
 	if (!serializer) {
 		return NULL;
 	}
 	return serializer;
 }
 
+struct ast_taskprocessor *ast_sip_create_serializer(void)
+{
+	return ast_sip_create_serializer_group(NULL);
+}
+
+/*!
+ * \internal
+ * \brief Shutdown the serializers in the default pool.
+ * \since 14.0.0
+ *
+ * \return Nothing
+ */
+static void serializer_pool_shutdown(void)
+{
+	int idx;
+
+	for (idx = 0; idx < SERIALIZER_POOL_SIZE; ++idx) {
+		ast_taskprocessor_unreference(serializer_pool[idx]);
+		serializer_pool[idx] = NULL;
+	}
+}
+
+/*!
+ * \internal
+ * \brief Setup the serializers in the default pool.
+ * \since 14.0.0
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int serializer_pool_setup(void)
+{
+	int idx;
+
+	for (idx = 0; idx < SERIALIZER_POOL_SIZE; ++idx) {
+		serializer_pool[idx] = ast_sip_create_serializer();
+		if (!serializer_pool[idx]) {
+			serializer_pool_shutdown();
+			return -1;
+		}
+	}
+	return 0;
+}
+
 int ast_sip_push_task(struct ast_taskprocessor *serializer, int (*sip_task)(void *), void *task_data)
 {
+	if (!serializer) {
+		unsigned int pos;
+
+		/*
+		 * Pick a serializer to use from the pool.
+		 *
+		 * Note: We don't care about any reentrancy behavior
+		 * when incrementing serializer_pool_pos.  If it gets
+		 * incorrectly incremented it doesn't matter.
+		 */
+		pos = serializer_pool_pos++;
+		pos %= SERIALIZER_POOL_SIZE;
+		serializer = serializer_pool[pos];
+	}
+
 	if (serializer) {
 		return ast_taskprocessor_push(serializer, sip_task, task_data);
 	} else {
@@ -2802,13 +3473,22 @@ struct sync_task_data {
 static int sync_task(void *data)
 {
 	struct sync_task_data *std = data;
+	int ret;
+
 	std->fail = std->task(std->task_data);
 
+	/*
+	 * Once we unlock std->lock after signaling, we cannot access
+	 * std again.  The thread waiting within
+	 * ast_sip_push_task_synchronous() is free to continue and
+	 * release its local variable (std).
+	 */
 	ast_mutex_lock(&std->lock);
 	std->complete = 1;
 	ast_cond_signal(&std->cond);
+	ret = std->fail;
 	ast_mutex_unlock(&std->lock);
-	return std->fail;
+	return ret;
 }
 
 int ast_sip_push_task_synchronous(struct ast_taskprocessor *serializer, int (*sip_task)(void *), void *task_data)
@@ -2826,18 +3506,10 @@ int ast_sip_push_task_synchronous(struct ast_taskprocessor *serializer, int (*si
 	std.task = sip_task;
 	std.task_data = task_data;
 
-	if (serializer) {
-		if (ast_taskprocessor_push(serializer, sync_task, &std)) {
-			ast_mutex_destroy(&std.lock);
-			ast_cond_destroy(&std.cond);
-			return -1;
-		}
-	} else {
-		if (ast_threadpool_push(sip_threadpool, sync_task, &std)) {
-			ast_mutex_destroy(&std.lock);
-			ast_cond_destroy(&std.cond);
-			return -1;
-		}
+	if (ast_sip_push_task(serializer, sync_task, &std)) {
+		ast_mutex_destroy(&std.lock);
+		ast_cond_destroy(&std.cond);
+		return -1;
 	}
 
 	ast_mutex_lock(&std.lock);
@@ -2970,8 +3642,13 @@ static pj_bool_t supplement_on_rx_request(pjsip_rx_data *rdata)
 
 	AST_RWLIST_RDLOCK(&supplements);
 	AST_LIST_TRAVERSE(&supplements, supplement, next) {
-		if (supplement->incoming_request && does_method_match(&rdata->msg_info.msg->line.req.method.name, supplement->method)) {
-			supplement->incoming_request(ast_pjsip_rdata_get_endpoint(rdata), rdata);
+		if (supplement->incoming_request
+			&& does_method_match(&rdata->msg_info.msg->line.req.method.name, supplement->method)) {
+			struct ast_sip_endpoint *endpoint;
+
+			endpoint = ast_pjsip_rdata_get_endpoint(rdata);
+			supplement->incoming_request(endpoint, rdata);
+			ao2_cleanup(endpoint);
 		}
 	}
 	AST_RWLIST_UNLOCK(&supplements);
@@ -2979,7 +3656,7 @@ static pj_bool_t supplement_on_rx_request(pjsip_rx_data *rdata)
 	return PJ_FALSE;
 }
 
-int ast_sip_send_response(pjsip_response_addr *res_addr, pjsip_tx_data *tdata, struct ast_sip_endpoint *sip_endpoint)
+static void supplement_outgoing_response(pjsip_tx_data *tdata, struct ast_sip_endpoint *sip_endpoint)
 {
 	struct ast_sip_supplement *supplement;
 	pjsip_cseq_hdr *cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
@@ -2995,10 +3672,43 @@ int ast_sip_send_response(pjsip_response_addr *res_addr, pjsip_tx_data *tdata, s
 
 	ast_sip_mod_data_set(tdata->pool, tdata->mod_data, supplement_module.id, MOD_DATA_CONTACT, NULL);
 	ao2_cleanup(contact);
+}
+
+int ast_sip_send_response(pjsip_response_addr *res_addr, pjsip_tx_data *tdata, struct ast_sip_endpoint *sip_endpoint)
+{
+	supplement_outgoing_response(tdata, sip_endpoint);
 
 	return pjsip_endpt_send_response(ast_sip_get_pjsip_endpoint(), res_addr, tdata, NULL, NULL);
 }
 
+int ast_sip_send_stateful_response(pjsip_rx_data *rdata, pjsip_tx_data *tdata, struct ast_sip_endpoint *sip_endpoint)
+{
+	pjsip_transaction *tsx;
+
+	if (pjsip_tsx_create_uas(NULL, rdata, &tsx) != PJ_SUCCESS) {
+		struct ast_sip_contact *contact;
+
+		/* ast_sip_create_response bumps the refcount of the contact and adds it to the tdata.
+		 * We'll leak that reference if we don't get rid of it here.
+		 */
+		contact = ast_sip_mod_data_get(tdata->mod_data, supplement_module.id, MOD_DATA_CONTACT);
+		ao2_cleanup(contact);
+		ast_sip_mod_data_set(tdata->pool, tdata->mod_data, supplement_module.id, MOD_DATA_CONTACT, NULL);
+		pjsip_tx_data_dec_ref(tdata);
+		return -1;
+	}
+	pjsip_tsx_recv_msg(tsx, rdata);
+
+	supplement_outgoing_response(tdata, sip_endpoint);
+
+	if (pjsip_tsx_send_msg(tsx, tdata) != PJ_SUCCESS) {
+		pjsip_tx_data_dec_ref(tdata);
+		return -1;
+	}
+
+	return 0;
+}
+
 int ast_sip_create_response(const pjsip_rx_data *rdata, int st_code,
 	struct ast_sip_contact *contact, pjsip_tx_data **tdata)
 {
@@ -3011,6 +3721,30 @@ int ast_sip_create_response(const pjsip_rx_data *rdata, int st_code,
 	return res;
 }
 
+int ast_sip_get_host_ip(int af, pj_sockaddr *addr)
+{
+	if (af == pj_AF_INET() && !ast_strlen_zero(host_ip_ipv4_string)) {
+		pj_sockaddr_copy_addr(addr, &host_ip_ipv4);
+		return 0;
+	} else if (af == pj_AF_INET6() && !ast_strlen_zero(host_ip_ipv6_string)) {
+		pj_sockaddr_copy_addr(addr, &host_ip_ipv6);
+		return 0;
+	}
+
+	return -1;
+}
+
+const char *ast_sip_get_host_ip_string(int af)
+{
+	if (af == pj_AF_INET()) {
+		return host_ip_ipv4_string;
+	} else if (af == pj_AF_INET6()) {
+		return host_ip_ipv6_string;
+	}
+
+	return NULL;
+}
+
 static void remove_request_headers(pjsip_endpoint *endpt)
 {
 	const pjsip_hdr *request_headers = pjsip_endpt_get_request_headers(endpt);
@@ -3023,6 +3757,62 @@ static void remove_request_headers(pjsip_endpoint *endpt)
 	}
 }
 
+long ast_sip_threadpool_queue_size(void)
+{
+	return ast_threadpool_queue_size(sip_threadpool);
+}
+
+AST_TEST_DEFINE(xml_sanitization_end_null)
+{
+	char sanitized[8];
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "xml_sanitization_end_null";
+		info->category = "/res/res_pjsip/";
+		info->summary = "Ensure XML sanitization works as expected with a long string";
+		info->description = "This test sanitizes a string which exceeds the output\n"
+			"buffer size. Once done the string is confirmed to be NULL terminated.";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	ast_sip_sanitize_xml("aaaaaaaaaaaa", sanitized, sizeof(sanitized));
+	if (sanitized[7] != '\0') {
+		ast_test_status_update(test, "Sanitized XML string is not null-terminated when it should be\n");
+		return AST_TEST_FAIL;
+	}
+
+	return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(xml_sanitization_exceeds_buffer)
+{
+	char sanitized[8];
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "xml_sanitization_exceeds_buffer";
+		info->category = "/res/res_pjsip/";
+		info->summary = "Ensure XML sanitization does not exceed buffer when output won't fit";
+		info->description = "This test sanitizes a string which before sanitization would\n"
+			"fit within the output buffer. After sanitization, however, the string would\n"
+			"exceed the buffer. Once done the string is confirmed to be NULL terminated.";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	ast_sip_sanitize_xml("<><><>&", sanitized, sizeof(sanitized));
+	if (sanitized[7] != '\0') {
+		ast_test_status_update(test, "Sanitized XML string is not null-terminated when it should be\n");
+		return AST_TEST_FAIL;
+	}
+
+	return AST_TEST_PASS;
+}
+
 /*!
  * \internal
  * \brief Reload configuration within a PJSIP thread
@@ -3074,6 +3864,16 @@ static int load_module(void)
 		return AST_MODULE_LOAD_DECLINE;
 	}
 
+	if (!pj_gethostip(pj_AF_INET(), &host_ip_ipv4)) {
+		pj_sockaddr_print(&host_ip_ipv4, host_ip_ipv4_string, sizeof(host_ip_ipv4_string), 2);
+		ast_verb(3, "Local IPv4 address determined to be: %s\n", host_ip_ipv4_string);
+	}
+
+	if (!pj_gethostip(pj_AF_INET6(), &host_ip_ipv6)) {
+		pj_sockaddr_print(&host_ip_ipv6, host_ip_ipv6_string, sizeof(host_ip_ipv6_string), 2);
+		ast_verb(3, "Local IPv6 address determined to be: %s\n", host_ip_ipv6_string);
+	}
+
 	if (ast_sip_initialize_system()) {
 		ast_log(LOG_ERROR, "Failed to initialize SIP 'system' configuration section. Aborting load\n");
 		pj_pool_release(memory_pool);
@@ -3098,6 +3898,18 @@ static int load_module(void)
 		return AST_MODULE_LOAD_DECLINE;
 	}
 
+	if (serializer_pool_setup()) {
+		ast_log(LOG_ERROR, "Failed to create SIP serializer pool. Aborting load\n");
+		ast_threadpool_shutdown(sip_threadpool);
+		ast_sip_destroy_system();
+		pj_pool_release(memory_pool);
+		memory_pool = NULL;
+		pjsip_endpt_destroy(ast_pjsip_endpoint);
+		ast_pjsip_endpoint = NULL;
+		pj_caching_pool_destroy(&caching_pool);
+		return AST_MODULE_LOAD_DECLINE;
+	}
+
 	ast_sip_initialize_dns();
 
 	pjsip_tsx_layer_init_module(ast_pjsip_endpoint);
@@ -3146,7 +3958,7 @@ static int load_module(void)
 		return AST_MODULE_LOAD_DECLINE;
 	}
 
-	if (ast_sip_register_service(&supplement_module)) {
+	if (internal_sip_register_service(&supplement_module)) {
 		ast_log(LOG_ERROR, "Failed to initialize supplement hooks. Aborting load\n");
 		ast_sip_destroy_distributor();
 		ast_res_pjsip_destroy_configuration();
@@ -3161,9 +3973,9 @@ static int load_module(void)
 		return AST_MODULE_LOAD_DECLINE;
 	}
 
-	if (ast_sip_initialize_outbound_authentication()) {
+	if (internal_sip_initialize_outbound_authentication()) {
 		ast_log(LOG_ERROR, "Failed to initialize outbound authentication. Aborting load\n");
-		ast_sip_unregister_service(&supplement_module);
+		internal_sip_unregister_service(&supplement_module);
 		ast_sip_destroy_distributor();
 		ast_res_pjsip_destroy_configuration();
 		ast_sip_destroy_global_headers();
@@ -3178,15 +3990,21 @@ static int load_module(void)
 	}
 
 	ast_res_pjsip_init_options_handling(0);
+	ast_cli_register_multiple(cli_commands, ARRAY_LEN(cli_commands));
 
-	ast_module_ref(ast_module_info->self);
+	AST_TEST_REGISTER(xml_sanitization_end_null);
+	AST_TEST_REGISTER(xml_sanitization_exceeds_buffer);
 
 	return AST_MODULE_LOAD_SUCCESS;
 }
 
 static int reload_module(void)
 {
-	if (ast_sip_push_task(NULL, reload_configuration_task, NULL)) {
+	/*
+	 * We must wait for the reload to complete so multiple
+	 * reloads cannot happen at the same time.
+	 */
+	if (ast_sip_push_task_synchronous(NULL, reload_configuration_task, NULL)) {
 		ast_log(LOG_WARNING, "Failed to reload PJSIP\n");
 		return -1;
 	}
@@ -3194,9 +4012,43 @@ static int reload_module(void)
 	return 0;
 }
 
+static int unload_pjsip(void *data)
+{
+	ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands));
+	ast_res_pjsip_cleanup_options_handling();
+	internal_sip_destroy_outbound_authentication();
+	ast_sip_destroy_distributor();
+	ast_res_pjsip_destroy_configuration();
+	ast_sip_destroy_system();
+	ast_sip_destroy_global_headers();
+	internal_sip_unregister_service(&supplement_module);
+	if (monitor_thread) {
+		stop_monitor_thread();
+	}
+	if (memory_pool) {
+		pj_pool_release(memory_pool);
+		memory_pool = NULL;
+	}
+	ast_pjsip_endpoint = NULL;
+	pj_caching_pool_destroy(&caching_pool);
+	pj_shutdown();
+	return 0;
+}
+
 static int unload_module(void)
 {
-	/* This will never get called as this module can't be unloaded */
+	AST_TEST_UNREGISTER(xml_sanitization_end_null);
+	AST_TEST_UNREGISTER(xml_sanitization_exceeds_buffer);
+
+	/* The thread this is called from cannot call PJSIP/PJLIB functions,
+	 * so we have to push the work to the threadpool to handle
+	 */
+	ast_sip_push_task_synchronous(NULL, unload_pjsip, NULL);
+
+	serializer_pool_shutdown();
+	ast_threadpool_shutdown(sip_threadpool);
+
+	ast_sip_destroy_cli();
 	return 0;
 }
 
diff --git a/res/res_pjsip/config_auth.c b/res/res_pjsip/config_auth.c
index fcab6a8..9160e67 100644
--- a/res/res_pjsip/config_auth.c
+++ b/res/res_pjsip/config_auth.c
@@ -195,13 +195,12 @@ static struct ast_sip_endpoint_formatter endpoint_auth_formatter = {
 	.format_ami = format_ami_endpoint_auth
 };
 
-static struct ao2_container *cli_get_container(void)
+static struct ao2_container *cli_get_container(const char *regex)
 {
 	RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup);
 	struct ao2_container *s_container;
 
-	container = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "auth",
-		AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+	container = ast_sorcery_retrieve_by_regex(ast_sip_get_sorcery(), "auth", regex);
 	if (!container) {
 		return NULL;
 	}
@@ -272,12 +271,14 @@ static int cli_print_body(void *obj, void *arg, int flags)
 static struct ast_cli_entry cli_commands[] = {
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "List PJSIP Auths",
 		.command = "pjsip list auths",
-		.usage = "Usage: pjsip list auths\n"
-				 "       List the configured PJSIP Auths\n"),
+		.usage = "Usage: pjsip list auths [ like <pattern> ]\n"
+				"       List the configured PJSIP Auths\n"
+				"       Optional regular expression pattern is used to filter the list.\n"),
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Auths",
 		.command = "pjsip show auths",
-		.usage = "Usage: pjsip show auths\n"
-				 "       Show the configured PJSIP Auths\n"),
+		.usage = "Usage: pjsip show auths [ like <pattern> ]\n"
+				"       Show the configured PJSIP Auths\n"
+				"       Optional regular expression pattern is used to filter the list.\n"),
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Auth",
 		.command = "pjsip show auth",
 		.usage = "Usage: pjsip show auth <id>\n"
@@ -312,7 +313,7 @@ int ast_sip_initialize_sorcery_auth(void)
 	ast_sorcery_object_field_register_custom(sorcery, SIP_SORCERY_AUTH_TYPE, "auth_type",
 			"userpass", auth_type_handler, auth_type_to_str, NULL, 0, 0);
 
-	ast_sip_register_endpoint_formatter(&endpoint_auth_formatter);
+	internal_sip_register_endpoint_formatter(&endpoint_auth_formatter);
 
 	cli_formatter = ao2_alloc(sizeof(struct ast_sip_cli_formatter_entry), NULL);
 	if (!cli_formatter) {
@@ -337,6 +338,7 @@ int ast_sip_destroy_sorcery_auth(void)
 {
 	ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands));
 	ast_sip_unregister_cli_formatter(cli_formatter);
+	internal_sip_unregister_endpoint_formatter(&endpoint_auth_formatter);
 
 	return 0;
 }
diff --git a/res/res_pjsip/config_domain_aliases.c b/res/res_pjsip/config_domain_aliases.c
index 232adac..8feff05 100644
--- a/res/res_pjsip/config_domain_aliases.c
+++ b/res/res_pjsip/config_domain_aliases.c
@@ -21,6 +21,7 @@
 #include "pjsip.h"
 #include "pjlib.h"
 #include "asterisk/res_pjsip.h"
+#include "include/res_pjsip_private.h"
 #include "asterisk/logger.h"
 #include "asterisk/sorcery.h"
 
diff --git a/res/res_pjsip/config_global.c b/res/res_pjsip/config_global.c
index 8e2cb2a..ef706f0 100644
--- a/res/res_pjsip/config_global.c
+++ b/res/res_pjsip/config_global.c
@@ -25,12 +25,18 @@
 #include "include/res_pjsip_private.h"
 #include "asterisk/sorcery.h"
 #include "asterisk/ast_version.h"
+#include "asterisk/res_pjsip_cli.h"
 
 #define DEFAULT_MAX_FORWARDS 70
+#define DEFAULT_KEEPALIVE_INTERVAL 0
 #define DEFAULT_USERAGENT_PREFIX "Asterisk PBX"
 #define DEFAULT_OUTBOUND_ENDPOINT "default_outbound_endpoint"
+#define DEFAULT_DEBUG "no"
+#define DEFAULT_ENDPOINT_IDENTIFIER_ORDER "ip,username,anonymous"
+#define DEFAULT_MAX_INITIAL_QUALIFY_TIME 0
+#define DEFAULT_FROM_USER "asterisk"
 
-static char default_useragent[128];
+static char default_useragent[256];
 
 struct global_config {
 	SORCERY_OBJECT(details);
@@ -39,9 +45,17 @@ struct global_config {
 		AST_STRING_FIELD(default_outbound_endpoint);
 		/*! Debug logging yes|no|host */
 		AST_STRING_FIELD(debug);
+		/*! Order by which endpoint identifiers are checked (comma separated list) */
+		AST_STRING_FIELD(endpoint_identifier_order);
+		/*! User name to place in From header if there is no better option */
+		AST_STRING_FIELD(default_from_user);
 	);
 	/* Value to put in Max-Forwards header */
 	unsigned int max_forwards;
+	/* The interval at which to send keep alive messages to active connection-oriented transports */
+	unsigned int keep_alive_interval;
+	/* The maximum time for all contacts to be qualified at startup */
+	unsigned int max_initial_qualify_time;
 };
 
 static void global_destructor(void *obj)
@@ -53,9 +67,11 @@ static void global_destructor(void *obj)
 
 static void *global_alloc(const char *name)
 {
-	struct global_config *cfg = ast_sorcery_generic_alloc(sizeof(*cfg), global_destructor);
+	struct global_config *cfg;
 
-	if (!cfg || ast_string_field_init(cfg, 80)) {
+	cfg = ast_sorcery_generic_alloc(sizeof(*cfg), global_destructor);
+	if (!cfg || ast_string_field_init(cfg, 100)) {
+		ao2_cleanup(cfg);
 		return NULL;
 	}
 
@@ -77,48 +93,194 @@ static int global_apply(const struct ast_sorcery *sorcery, void *obj)
 
 static struct global_config *get_global_cfg(void)
 {
-	RAII_VAR(struct ao2_container *, globals, ast_sorcery_retrieve_by_fields(
-			 ast_sip_get_sorcery(), "global", AST_RETRIEVE_FLAG_MULTIPLE,
-			 NULL), ao2_cleanup);
+	struct global_config *cfg;
+	struct ao2_container *globals;
 
+	globals = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "global",
+		AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
 	if (!globals) {
 		return NULL;
 	}
 
-	return ao2_find(globals, NULL, 0);
+	cfg = ao2_find(globals, NULL, 0);
+	ao2_ref(globals, -1);
+	return cfg;
 }
 
 char *ast_sip_global_default_outbound_endpoint(void)
 {
-	RAII_VAR(struct global_config *, cfg, get_global_cfg(), ao2_cleanup);
+	char *str;
+	struct global_config *cfg;
 
+	cfg = get_global_cfg();
 	if (!cfg) {
-		return NULL;
+		return ast_strdup(DEFAULT_OUTBOUND_ENDPOINT);
 	}
 
-	return ast_strdup(cfg->default_outbound_endpoint);
+	str = ast_strdup(cfg->default_outbound_endpoint);
+	ao2_ref(cfg, -1);
+	return str;
 }
 
 char *ast_sip_get_debug(void)
 {
 	char *res;
-	struct global_config *cfg = get_global_cfg();
+	struct global_config *cfg;
 
+	cfg = get_global_cfg();
 	if (!cfg) {
-		return ast_strdup("no");
+		return ast_strdup(DEFAULT_DEBUG);
 	}
 
 	res = ast_strdup(cfg->debug);
 	ao2_ref(cfg, -1);
+	return res;
+}
 
+char *ast_sip_get_endpoint_identifier_order(void)
+{
+	char *res;
+	struct global_config *cfg;
+
+	cfg = get_global_cfg();
+	if (!cfg) {
+		return ast_strdup(DEFAULT_ENDPOINT_IDENTIFIER_ORDER);
+	}
+
+	res = ast_strdup(cfg->endpoint_identifier_order);
+	ao2_ref(cfg, -1);
 	return res;
 }
 
+unsigned int ast_sip_get_keep_alive_interval(void)
+{
+	unsigned int interval;
+	struct global_config *cfg;
+
+	cfg = get_global_cfg();
+	if (!cfg) {
+		return DEFAULT_KEEPALIVE_INTERVAL;
+	}
+
+	interval = cfg->keep_alive_interval;
+	ao2_ref(cfg, -1);
+	return interval;
+}
+
+unsigned int ast_sip_get_max_initial_qualify_time(void)
+{
+	unsigned int time;
+	struct global_config *cfg;
+
+	cfg = get_global_cfg();
+	if (!cfg) {
+		return DEFAULT_MAX_INITIAL_QUALIFY_TIME;
+	}
+
+	time = cfg->max_initial_qualify_time;
+	ao2_ref(cfg, -1);
+	return time;
+}
+
+void ast_sip_get_default_from_user(char *from_user, size_t size)
+{
+	struct global_config *cfg;
+
+	cfg = get_global_cfg();
+	if (!cfg) {
+		ast_copy_string(from_user, DEFAULT_FROM_USER, size);
+	} else {
+		ast_copy_string(from_user, cfg->default_from_user, size);
+		ao2_ref(cfg, -1);
+	}
+}
+
+/*!
+ * \internal
+ * \brief Observer to set default global object if none exist.
+ *
+ * \param name Module name owning the sorcery instance.
+ * \param sorcery Instance being observed.
+ * \param object_type Name of object being observed.
+ * \param reloaded Non-zero if the object is being reloaded.
+ *
+ * \return Nothing
+ */
+static void global_loaded_observer(const char *name, const struct ast_sorcery *sorcery, const char *object_type, int reloaded)
+{
+	struct ao2_container *globals;
+	struct global_config *cfg;
+
+	if (strcmp(object_type, "global")) {
+		/* Not interested */
+		return;
+	}
+
+	globals = ast_sorcery_retrieve_by_fields(sorcery, "global",
+		AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+	if (globals) {
+		int count;
+
+		count = ao2_container_count(globals);
+		ao2_ref(globals, -1);
+
+		if (1 < count) {
+			ast_log(LOG_ERROR,
+				"At most one pjsip.conf type=global object can be defined.  You have %d defined.\n",
+				count);
+			return;
+		}
+		if (count) {
+			return;
+		}
+	}
+
+	ast_debug(1, "No pjsip.conf type=global object exists so applying defaults.\n");
+	cfg = ast_sorcery_alloc(sorcery, "global", NULL);
+	if (!cfg) {
+		return;
+	}
+	global_apply(sorcery, cfg);
+	ao2_ref(cfg, -1);
+}
+
+static const struct ast_sorcery_instance_observer observer_callbacks_global = {
+	.object_type_loaded = global_loaded_observer,
+};
+
+int sip_cli_print_global(struct ast_sip_cli_context *context)
+{
+	struct global_config *cfg = get_global_cfg();
+
+	if (!cfg) {
+		cfg = ast_sorcery_alloc(ast_sip_get_sorcery(), "global", NULL);
+		if (!cfg) {
+			return -1;
+		}
+	}
+
+	ast_str_append(&context->output_buffer, 0, "\nGlobal Settings:\n\n");
+	ast_sip_cli_print_sorcery_objectset(cfg, context, 0);
+
+	ao2_ref(cfg, -1);
+	return 0;
+}
+
+int ast_sip_destroy_sorcery_global(void)
+{
+	struct ast_sorcery *sorcery = ast_sip_get_sorcery();
+
+	ast_sorcery_instance_observer_remove(sorcery, &observer_callbacks_global);
+
+	return 0;
+}
+
 int ast_sip_initialize_sorcery_global(void)
 {
 	struct ast_sorcery *sorcery = ast_sip_get_sorcery();
 
-	snprintf(default_useragent, sizeof(default_useragent), "%s %s", DEFAULT_USERAGENT_PREFIX, ast_get_version());
+	snprintf(default_useragent, sizeof(default_useragent), "%s %s",
+		DEFAULT_USERAGENT_PREFIX, ast_get_version());
 
 	ast_sorcery_apply_default(sorcery, "global", "config", "pjsip.conf,criteria=type=global");
 
@@ -127,14 +289,31 @@ int ast_sip_initialize_sorcery_global(void)
 	}
 
 	ast_sorcery_object_field_register(sorcery, "global", "type", "", OPT_NOOP_T, 0, 0);
-	ast_sorcery_object_field_register(sorcery, "global", "max_forwards", __stringify(DEFAULT_MAX_FORWARDS),
-			OPT_UINT_T, 0, FLDSET(struct global_config, max_forwards));
+	ast_sorcery_object_field_register(sorcery, "global", "max_forwards",
+		__stringify(DEFAULT_MAX_FORWARDS),
+		OPT_UINT_T, 0, FLDSET(struct global_config, max_forwards));
 	ast_sorcery_object_field_register(sorcery, "global", "user_agent", default_useragent,
-			OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, useragent));
-	ast_sorcery_object_field_register(sorcery, "global", "default_outbound_endpoint", DEFAULT_OUTBOUND_ENDPOINT,
-			OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, default_outbound_endpoint));
-	ast_sorcery_object_field_register(sorcery, "global", "debug", "no",
-			OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, debug));
+		OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, useragent));
+	ast_sorcery_object_field_register(sorcery, "global", "default_outbound_endpoint",
+		DEFAULT_OUTBOUND_ENDPOINT,
+		OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, default_outbound_endpoint));
+	ast_sorcery_object_field_register(sorcery, "global", "debug", DEFAULT_DEBUG,
+		OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, debug));
+	ast_sorcery_object_field_register(sorcery, "global", "endpoint_identifier_order",
+		DEFAULT_ENDPOINT_IDENTIFIER_ORDER,
+		OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, endpoint_identifier_order));
+	ast_sorcery_object_field_register(sorcery, "global", "keep_alive_interval",
+		__stringify(DEFAULT_KEEPALIVE_INTERVAL),
+		OPT_UINT_T, 0, FLDSET(struct global_config, keep_alive_interval));
+	ast_sorcery_object_field_register(sorcery, "global", "max_initial_qualify_time",
+		__stringify(DEFAULT_MAX_INITIAL_QUALIFY_TIME),
+		OPT_UINT_T, 0, FLDSET(struct global_config, max_initial_qualify_time));
+	ast_sorcery_object_field_register(sorcery, "global", "default_from_user", DEFAULT_FROM_USER,
+		OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, default_from_user));
+
+	if (ast_sorcery_instance_observer_add(sorcery, &observer_callbacks_global)) {
+		return -1;
+	}
 
 	return 0;
 }
diff --git a/res/res_pjsip/config_system.c b/res/res_pjsip/config_system.c
index a41af90..dfd9240 100644
--- a/res/res_pjsip/config_system.c
+++ b/res/res_pjsip/config_system.c
@@ -26,6 +26,7 @@
 #include "include/res_pjsip_private.h"
 #include "asterisk/threadpool.h"
 #include "asterisk/dns.h"
+#include "asterisk/res_pjsip_cli.h"
 
 #define TIMER_T1_MIN 100
 #define DEFAULT_TIMER_T1 500
@@ -112,6 +113,40 @@ static int system_apply(const struct ast_sorcery *system_sorcery, void *obj)
 	return 0;
 }
 
+static struct system_config *get_system_cfg(void)
+{
+	struct system_config *cfg;
+	struct ao2_container *systems;
+	systems = ast_sorcery_retrieve_by_fields(system_sorcery, "system",
+		AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+
+	if (!systems) {
+		return NULL;
+	}
+
+	cfg = ao2_find(systems, NULL, 0);
+	ao2_ref(systems, -1);
+	return cfg;
+}
+
+int sip_cli_print_system(struct ast_sip_cli_context *context)
+{
+	struct system_config *cfg = get_system_cfg();
+
+	if (!cfg) {
+		cfg = ast_sorcery_alloc(system_sorcery, "system", NULL);
+		if (!cfg) {
+			return -1;
+		}
+	}
+
+	ast_str_append(&context->output_buffer, 0, "\nSystem Settings:\n\n");
+	ast_sip_cli_print_sorcery_objectset(cfg, context, 0);
+
+	ao2_ref(cfg, -1);
+	return 0;
+}
+
 int ast_sip_initialize_system(void)
 {
 	RAII_VAR(struct ao2_container *, system_configs, NULL, ao2_cleanup);
@@ -145,7 +180,7 @@ int ast_sip_initialize_system(void)
 			OPT_UINT_T, 0, FLDSET(struct system_config, threadpool.auto_increment));
 	ast_sorcery_object_field_register(system_sorcery, "system", "threadpool_idle_timeout", "60",
 			OPT_UINT_T, 0, FLDSET(struct system_config, threadpool.idle_timeout));
-	ast_sorcery_object_field_register(system_sorcery, "system", "threadpool_max_size", "0",
+	ast_sorcery_object_field_register(system_sorcery, "system", "threadpool_max_size", "50",
 			OPT_UINT_T, 0, FLDSET(struct system_config, threadpool.max_size));
 	ast_sorcery_object_field_register(system_sorcery, "system", "disable_tcp_switch", "yes",
 			OPT_BOOL_T, 1, FLDSET(struct system_config, disable_tcp_switch));
diff --git a/res/res_pjsip/config_transport.c b/res/res_pjsip/config_transport.c
index fe7612f..840824b 100644
--- a/res/res_pjsip/config_transport.c
+++ b/res/res_pjsip/config_transport.c
@@ -27,6 +27,7 @@
 #include "asterisk/astobj2.h"
 #include "asterisk/sorcery.h"
 #include "asterisk/acl.h"
+#include "asterisk/utils.h"
 #include "include/res_pjsip_private.h"
 #include "asterisk/http_websocket.h"
 
@@ -216,8 +217,49 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
 
 		res = pjsip_tcp_transport_start3(ast_sip_get_pjsip_endpoint(), &cfg, &transport->state->factory);
 	} else if (transport->type == AST_TRANSPORT_TLS) {
+		if (transport->async_operations > 1 && ast_compare_versions(pj_get_version(), "2.5.0") < 0) {
+			ast_log(LOG_ERROR, "Transport: %s: When protocol=tls and pjproject version < 2.5.0, async_operations can't be > 1\n",
+					ast_sorcery_object_get_id(obj));
+			return -1;
+		}
+		if (!ast_strlen_zero(transport->ca_list_file)) {
+			if (!ast_file_is_readable(transport->ca_list_file)) {
+				ast_log(LOG_ERROR, "Transport: %s: ca_list_file %s is either missing or not readable\n",
+						ast_sorcery_object_get_id(obj), transport->ca_list_file);
+				return -1;
+			}
+		}
 		transport->tls.ca_list_file = pj_str((char*)transport->ca_list_file);
+#ifdef HAVE_PJ_SSL_CERT_LOAD_FROM_FILES2
+		if (!ast_strlen_zero(transport->ca_list_path)) {
+			if (!ast_file_is_readable(transport->ca_list_path)) {
+				ast_log(LOG_ERROR, "Transport: %s: ca_list_path %s is either missing or not readable\n",
+						ast_sorcery_object_get_id(obj), transport->ca_list_path);
+				return -1;
+			}
+		}
+		transport->tls.ca_list_path = pj_str((char*)transport->ca_list_path);
+#else
+		if (!ast_strlen_zero(transport->ca_list_path)) {
+			ast_log(LOG_WARNING, "Asterisk has been built against a version of pjproject that does not "
+					"support the 'ca_list_path' option. Please upgrade to version 2.4 or later.\n");
+		}
+#endif
+		if (!ast_strlen_zero(transport->cert_file)) {
+			if (!ast_file_is_readable(transport->cert_file)) {
+				ast_log(LOG_ERROR, "Transport: %s: cert_file %s is either missing or not readable\n",
+						ast_sorcery_object_get_id(obj), transport->cert_file);
+				return -1;
+			}
+		}
 		transport->tls.cert_file = pj_str((char*)transport->cert_file);
+		if (!ast_strlen_zero(transport->privkey_file)) {
+			if (!ast_file_is_readable(transport->privkey_file)) {
+				ast_log(LOG_ERROR, "Transport: %s: privkey_file %s is either missing or not readable\n",
+						ast_sorcery_object_get_id(obj), transport->privkey_file);
+				return -1;
+			}
+		}
 		transport->tls.privkey_file = pj_str((char*)transport->privkey_file);
 		transport->tls.password = pj_str((char*)transport->password);
 		set_qos(transport, &transport->tls.qos_params);
@@ -369,7 +411,6 @@ static int transport_tls_method_handler(const struct aco_option *opt, struct ast
 }
 
 static const char *tls_method_map[] = {
-	[PJSIP_SSL_DEFAULT_METHOD] = "default",
 	[PJSIP_SSL_UNSPECIFIED_METHOD] = "unspecified",
 	[PJSIP_TLSV1_METHOD] = "tlsv1",
 	[PJSIP_SSLV2_METHOD] = "sslv2",
@@ -626,13 +667,13 @@ static int tos_to_str(const void *obj, const intptr_t *args, char **buf)
 	return 0;
 }
 
-static struct ao2_container *cli_get_container(void)
+static struct ao2_container *cli_get_container(const char *regex)
 {
 	RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup);
 	struct ao2_container *s_container;
 
-	container = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "transport",
-		AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+	container = ast_sorcery_retrieve_by_regex(ast_sip_get_sorcery(), "transport",
+		regex);
 	if (!container) {
 		return NULL;
 	}
@@ -713,12 +754,14 @@ static struct ast_cli_entry cli_commands[] = {
 	AST_CLI_DEFINE(handle_pjsip_list_ciphers, "List available OpenSSL cipher names"),
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "List PJSIP Transports",
 		.command = "pjsip list transports",
-		.usage = "Usage: pjsip list transports\n"
-				 "       List the configured PJSIP Transports\n"),
+		.usage = "Usage: pjsip list transports [ like <pattern> ]\n"
+				"       List the configured PJSIP Transports\n"
+				"       Optional regular expression pattern is used to filter the list.\n"),
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Transports",
 		.command = "pjsip show transports",
-		.usage = "Usage: pjsip show transports\n"
-				 "       Show the configured PJSIP Transport\n"),
+		.usage = "Usage: pjsip show transports [ like <pattern> ]\n"
+				"       Show the configured PJSIP Transport\n"
+				"       Optional regular expression pattern is used to filter the list.\n"),
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Transport",
 		.command = "pjsip show transport",
 		.usage = "Usage: pjsip show transport <id>\n"
@@ -743,6 +786,7 @@ int ast_sip_initialize_sorcery_transport(void)
 	ast_sorcery_object_field_register_custom(sorcery, "transport", "bind", "", transport_bind_handler, transport_bind_to_str, NULL, 0, 0);
 	ast_sorcery_object_field_register(sorcery, "transport", "async_operations", "1", OPT_UINT_T, 0, FLDSET(struct ast_sip_transport, async_operations));
 	ast_sorcery_object_field_register(sorcery, "transport", "ca_list_file", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, ca_list_file));
+	ast_sorcery_object_field_register(sorcery, "transport", "ca_list_path", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, ca_list_path));
 	ast_sorcery_object_field_register(sorcery, "transport", "cert_file", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, cert_file));
 	ast_sorcery_object_field_register(sorcery, "transport", "priv_key_file", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, privkey_file));
 	ast_sorcery_object_field_register(sorcery, "transport", "password", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, password));
@@ -760,7 +804,7 @@ int ast_sip_initialize_sorcery_transport(void)
 	ast_sorcery_object_field_register(sorcery, "transport", "cos", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_transport, cos));
 	ast_sorcery_object_field_register(sorcery, "transport", "websocket_write_timeout", AST_DEFAULT_WEBSOCKET_WRITE_TIMEOUT_STR, OPT_INT_T, PARSE_IN_RANGE, FLDSET(struct ast_sip_transport, write_timeout), 1, INT_MAX);
 
-	ast_sip_register_endpoint_formatter(&endpoint_transport_formatter);
+	internal_sip_register_endpoint_formatter(&endpoint_transport_formatter);
 
 	cli_formatter = ao2_alloc(sizeof(struct ast_sip_cli_formatter_entry), NULL);
 	if (!cli_formatter) {
@@ -786,5 +830,7 @@ int ast_sip_destroy_sorcery_transport(void)
 	ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands));
 	ast_sip_unregister_cli_formatter(cli_formatter);
 
+	internal_sip_unregister_endpoint_formatter(&endpoint_transport_formatter);
+
 	return 0;
 }
diff --git a/res/res_pjsip/include/res_pjsip_private.h b/res/res_pjsip/include/res_pjsip_private.h
index fa37c8c..c1f7e23 100644
--- a/res/res_pjsip/include/res_pjsip_private.h
+++ b/res/res_pjsip/include/res_pjsip_private.h
@@ -1,5 +1,5 @@
 /*
- * res_pjsip.h
+ * res_pjsip_private.h
  *
  *  Created on: Jan 25, 2013
  *      Author: mjordan
@@ -8,28 +8,168 @@
 #ifndef RES_PJSIP_PRIVATE_H_
 #define RES_PJSIP_PRIVATE_H_
 
+/*!
+ * \todo XXX Functions prototyped in this file that begin with "ast_sip_"
+ * need to be renamed so res_pjsip.so does not export the names outside
+ * of the module.
+ */
+
 #include "asterisk/module.h"
 #include "asterisk/compat.h"
 
 struct ao2_container;
 struct ast_threadpool_options;
+struct ast_sip_cli_context;
 
 /*!
+ * \internal
  * \brief Initialize the configuration for res_pjsip
  */
 int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_module_info);
 
 /*!
+ * \internal
  * \brief Annihilate the configuration objects
  */
 void ast_res_pjsip_destroy_configuration(void);
 
 /*!
+ * \internal
  * \brief Reload the configuration
  */
 int ast_res_pjsip_reload_configuration(void);
 
 /*!
+ * \internal
+ * \brief Initialize transport support on a sorcery instance
+ *
+ * \retval -1 failure
+ * \retval 0 success
+ */
+int ast_sip_initialize_sorcery_transport(void);
+
+/*!
+ * \internal
+ * \brief Destroy transport support on a sorcery instance
+ *
+ * \retval -1 failure
+ * \retval 0 success
+ */
+int ast_sip_destroy_sorcery_transport(void);
+
+/*!
+ * \internal
+ * \brief Initialize qualify support on a sorcery instance
+ *
+ * \retval -1 failure
+ * \retval 0 success
+ */
+int ast_sip_initialize_sorcery_qualify(void);
+
+/*!
+ * \internal
+ * \brief Initialize location support on a sorcery instance
+ *
+ * \retval -1 failure
+ * \retval 0 success
+ */
+int ast_sip_initialize_sorcery_location(void);
+
+/*!
+ * \internal
+ * \brief Destroy location support on a sorcery instance
+ *
+ * \retval -1 failure
+ * \retval 0 success
+ */
+int ast_sip_destroy_sorcery_location(void);
+
+/*!
+ * \internal
+ * \brief Initialize domain aliases support on a sorcery instance
+ *
+ * \retval -1 failure
+ * \retval 0 success
+ */
+int ast_sip_initialize_sorcery_domain_alias(void);
+
+/*!
+ * \internal
+ * \brief Initialize authentication support on a sorcery instance
+ *
+ * \retval -1 failure
+ * \retval 0 success
+ */
+int ast_sip_initialize_sorcery_auth(void);
+
+/*!
+ * \internal
+ * \brief Destroy authentication support on a sorcery instance
+ *
+ * \retval -1 failure
+ * \retval 0 success
+ */
+int ast_sip_destroy_sorcery_auth(void);
+
+/*!
+ * \internal
+ * \brief Initialize the distributor module
+ *
+ * The distributor module is responsible for taking an incoming
+ * SIP message and placing it into the threadpool. Once in the threadpool,
+ * the distributor will perform endpoint lookups and authentication, and
+ * then distribute the message up the stack to any further modules.
+ *
+ * \retval -1 Failure
+ * \retval 0 Success
+ */
+int ast_sip_initialize_distributor(void);
+
+/*!
+ * \internal
+ * \brief Destruct the distributor module.
+ *
+ * Unregisters pjsip modules and cleans up any allocated resources.
+ */
+void ast_sip_destroy_distributor(void);
+
+/*!
+ * \internal
+ * \brief Initialize global type on a sorcery instance
+ *
+ * \retval -1 failure
+ * \retval 0 success
+ */
+int ast_sip_initialize_sorcery_global(void);
+
+/*!
+ * \internal
+ * \brief Destroy global type on a sorcery instance
+ * \since 13.3.0
+ *
+ * \retval -1 failure
+ * \retval 0 success
+ */
+int ast_sip_destroy_sorcery_global(void);
+
+/*!
+ * \internal
+ * \brief Initialize global headers support
+ *
+ * \return Nothing
+ */
+void ast_sip_initialize_global_headers(void);
+
+/*!
+ * \internal
+ * \brief Destroy global headers support
+ *
+ * \return Nothing
+ */
+void ast_sip_destroy_global_headers(void);
+
+/*!
+ * \internal
  * \brief Initialize OPTIONS request handling.
  *
  * XXX This currently includes qualifying peers. It shouldn't.
@@ -44,6 +184,7 @@ int ast_res_pjsip_reload_configuration(void);
 int ast_res_pjsip_init_options_handling(int reload);
 
 /*!
+ * \internal
  * \brief Initialize transport storage for contacts.
  *
  * \retval 0 on success
@@ -52,14 +193,25 @@ int ast_res_pjsip_init_options_handling(int reload);
 int ast_res_pjsip_init_contact_transports(void);
 
 /*!
+ * \internal
  * \brief Initialize outbound authentication support
  *
  * \retval 0 Success
  * \retval non-zero Failure
  */
-int ast_sip_initialize_outbound_authentication(void);
+int internal_sip_initialize_outbound_authentication(void);
+
+/*!
+ * \internal
+ * \brief Destroy outbound authentication support
+ *
+ * \retval 0 Success
+ * \retval non-zero Failure
+ */
+void internal_sip_destroy_outbound_authentication(void);
 
 /*!
+ * \internal
  * \brief Initialize system configuration
  *
  * \retval 0 Success
@@ -68,16 +220,19 @@ int ast_sip_initialize_outbound_authentication(void);
 int ast_sip_initialize_system(void);
 
 /*!
+ * \internal
  * \brief Destroy system configuration
  */
 void ast_sip_destroy_system(void);
 
 /*!
+ * \internal
  * \brief Initialize nameserver configuration
  */
 void ast_sip_initialize_dns(void);
 
 /*!
+ * \internal
  * \brief Initialize global configuration
  *
  * \retval 0 Success
@@ -86,16 +241,19 @@ void ast_sip_initialize_dns(void);
 int ast_sip_initialize_global(void);
 
 /*!
+ * \internal
  * \brief Clean up res_pjsip options handling
  */
 void ast_res_pjsip_cleanup_options_handling(void);
 
 /*!
+ * \internal
  * \brief Get threadpool options
  */
 void sip_get_threadpool_options(struct ast_threadpool_options *threadpool_options);
 
 /*!
+ * \internal
  * \brief Retrieve the name of the default outbound endpoint.
  *
  * \note This returns a memory allocated copy of the name that
@@ -107,9 +265,57 @@ void sip_get_threadpool_options(struct ast_threadpool_options *threadpool_option
 char *ast_sip_global_default_outbound_endpoint(void);
 
 /*!
+ * \internal
  * \brief Functions for initializing and destroying the CLI.
  */
 int ast_sip_initialize_cli(void);
 void ast_sip_destroy_cli(void);
 
+/*!
+ * \internal
+ * \brief Add res_pjsip global configuration options to the cli context.
+ *
+ * \param context context to add options to
+ * \retval 0 Success, -1 on failure
+ */
+int sip_cli_print_global(struct ast_sip_cli_context *context);
+
+/*!
+ * \internal
+ * \brief Add res_pjsip system configuration options to the cli context.
+ *
+ * \param context context to add options to
+ * \retval 0 Success, -1 on failure
+ */
+int sip_cli_print_system(struct ast_sip_cli_context *context);
+
+/*!
+ * \internal
+ * \brief Used by res_pjsip.so to register a service without adding a self reference
+ */
+int internal_sip_register_service(pjsip_module *module);
+
+/*!
+ * \internal
+ * \brief Used by res_pjsip.so to unregister a service without removing a self reference
+ */
+int internal_sip_unregister_service(pjsip_module *module);
+
+/*!
+ * \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);
+
+/*!
+ * \internal
+ * \brief Used by res_pjsip.so to unregister a endpoint formatter without removing a self reference
+ */
+int internal_sip_unregister_endpoint_formatter(struct ast_sip_endpoint_formatter *obj);
+
+/*!
+ * \internal
+ * \brief Finds or creates contact_status for a contact
+ */
+struct ast_sip_contact_status *ast_res_pjsip_find_or_create_contact_status(const struct ast_sip_contact *contact);
 #endif /* RES_PJSIP_PRIVATE_H_ */
diff --git a/res/res_pjsip/location.c b/res/res_pjsip/location.c
index d036ffa..2908f6f 100644
--- a/res/res_pjsip/location.c
+++ b/res/res_pjsip/location.c
@@ -26,6 +26,7 @@
 #include "asterisk/sorcery.h"
 #include "include/res_pjsip_private.h"
 #include "asterisk/res_pjsip_cli.h"
+#include "asterisk/statsd.h"
 
 /*! \brief Destructor for AOR */
 static void aor_destroy(void *obj)
@@ -47,18 +48,61 @@ static void *aor_alloc(const char *name)
 	return aor;
 }
 
+/*! \brief Internal callback function which destroys the specified contact */
+static int destroy_contact(void *obj, void *arg, int flags)
+{
+	struct ast_sip_contact *contact = obj;
+
+	ast_sip_location_delete_contact(contact);
+
+	return CMP_MATCH;
+}
+
+static void aor_deleted_observer(const void *object)
+{
+	const struct ast_sip_aor *aor = object;
+	const char *aor_id = ast_sorcery_object_get_id(object);
+	/* Give enough space for ^ at the beginning and ;@ at the end, since that is our object naming scheme */
+	char regex[strlen(aor_id) + 4];
+	struct ao2_container *contacts;
+
+	if (aor->permanent_contacts) {
+		ao2_callback(aor->permanent_contacts, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, destroy_contact, NULL);
+	}
+
+	snprintf(regex, sizeof(regex), "^%s;@", aor_id);
+	if (!(contacts = ast_sorcery_retrieve_by_regex(ast_sip_get_sorcery(), "contact", regex))) {
+		return;
+	}
+	/* Destroy any contacts that may still exist that were made for this AoR */
+	ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, destroy_contact, NULL);
+
+	ao2_ref(contacts, -1);
+}
+
+/*! \brief Observer for contacts so state can be updated on respective endpoints */
+static const struct ast_sorcery_observer aor_observer = {
+	.deleted = aor_deleted_observer,
+};
+
+
 /*! \brief Destructor for contact */
 static void contact_destroy(void *obj)
 {
 	struct ast_sip_contact *contact = obj;
 
 	ast_string_field_free_memory(contact);
+	ast_free(contact->aor);
+	ao2_cleanup(contact->endpoint);
 }
 
 /*! \brief Allocator for contact */
 static void *contact_alloc(const char *name)
 {
 	struct ast_sip_contact *contact = ast_sorcery_generic_alloc(sizeof(*contact), contact_destroy);
+	char *id = ast_strdupa(name);
+	char *aor = id;
+	char *aor_separator = NULL;
 
 	if (!contact) {
 		return NULL;
@@ -69,6 +113,18 @@ static void *contact_alloc(const char *name)
 		return NULL;
 	}
 
+	/* Dynamic contacts are delimited with ";@" and static ones with "@@" */
+	if ((aor_separator = strstr(id, ";@")) || (aor_separator = strstr(id, "@@"))) {
+		*aor_separator = '\0';
+	}
+	ast_assert(aor_separator != NULL);
+
+	contact->aor = ast_strdup(aor);
+	if (!contact->aor) {
+		ao2_cleanup(contact);
+		return NULL;
+	}
+
 	return contact;
 }
 
@@ -101,12 +157,6 @@ static int contact_link_static(void *obj, void *arg, int flags)
 	return 0;
 }
 
-/*! \brief Simple callback function which returns immediately, used to grab the first contact of an AOR */
-static int contact_find_first(void *obj, void *arg, int flags)
-{
-	return CMP_MATCH | CMP_STOP;
-}
-
 struct ast_sip_contact *ast_sip_location_retrieve_first_aor_contact(const struct ast_sip_aor *aor)
 {
 	RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
@@ -117,7 +167,8 @@ struct ast_sip_contact *ast_sip_location_retrieve_first_aor_contact(const struct
 		return NULL;
 	}
 
-	contact = ao2_callback(contacts, 0, contact_find_first, NULL);
+	/* Get the first AOR contact in the container. */
+	contact = ao2_callback(contacts, 0, NULL, NULL);
 	return contact;
 }
 
@@ -144,46 +195,99 @@ struct ao2_container *ast_sip_location_retrieve_aor_contacts(const struct ast_si
 	return contacts;
 }
 
-struct ast_sip_contact *ast_sip_location_retrieve_contact_from_aor_list(const char *aor_list)
+void ast_sip_location_retrieve_contact_and_aor_from_list(const char *aor_list, struct ast_sip_aor **aor,
+	struct ast_sip_contact **contact)
 {
 	char *aor_name;
 	char *rest;
-	struct ast_sip_contact *contact = NULL;
 
 	/* If the location is still empty we have nowhere to go */
 	if (ast_strlen_zero(aor_list) || !(rest = ast_strdupa(aor_list))) {
 		ast_log(LOG_WARNING, "Unable to determine contacts from empty aor list\n");
-		return NULL;
+		return;
 	}
 
+	*aor = NULL;
+	*contact = NULL;
+
 	while ((aor_name = strsep(&rest, ","))) {
-		RAII_VAR(struct ast_sip_aor *, aor, ast_sip_location_retrieve_aor(aor_name), ao2_cleanup);
+		*aor = ast_sip_location_retrieve_aor(aor_name);
 
-		if (!aor) {
+		if (!(*aor)) {
 			continue;
 		}
-		contact = ast_sip_location_retrieve_first_aor_contact(aor);
+		*contact = ast_sip_location_retrieve_first_aor_contact(*aor);
 		/* If a valid contact is available use its URI for dialing */
-		if (contact) {
+		if (*contact) {
 			break;
 		}
+
+		ao2_ref(*aor, -1);
+		*aor = NULL;
 	}
+}
+
+struct ast_sip_contact *ast_sip_location_retrieve_contact_from_aor_list(const char *aor_list)
+{
+	struct ast_sip_aor *aor;
+	struct ast_sip_contact *contact;
+
+	ast_sip_location_retrieve_contact_and_aor_from_list(aor_list, &aor, &contact);
+
+	ao2_cleanup(aor);
 
 	return contact;
 }
 
+static int permanent_uri_sort_fn(const void *obj_left, const void *obj_right, int flags);
+static int cli_contact_populate_container(void *obj, void *arg, int flags);
+
+static int gather_contacts_for_aor(void *obj, void *arg, int flags)
+{
+	struct ao2_container *aor_contacts;
+	struct ast_sip_aor *aor = obj;
+	struct ao2_container *container = arg;
+
+	aor_contacts = ast_sip_location_retrieve_aor_contacts(aor);
+	if (!aor_contacts) {
+		return 0;
+	}
+	ao2_callback(aor_contacts, OBJ_MULTIPLE | OBJ_NODATA, cli_contact_populate_container,
+		container);
+	ao2_ref(aor_contacts, -1);
+	return CMP_MATCH;
+}
+
+struct ao2_container *ast_sip_location_retrieve_contacts_from_aor_list(const char *aor_list)
+{
+	struct ao2_container *contacts;
+
+	contacts = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK,
+		AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT, permanent_uri_sort_fn, NULL);
+	if (!contacts) {
+		return NULL;
+	}
+
+	ast_sip_for_each_aor(aor_list, gather_contacts_for_aor, contacts);
+
+	return contacts;
+}
+
 struct ast_sip_contact *ast_sip_location_retrieve_contact(const char *contact_name)
 {
 	return ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "contact", contact_name);
 }
 
 int ast_sip_location_add_contact(struct ast_sip_aor *aor, const char *uri,
-		struct timeval expiration_time, const char *path_info, const char *user_agent)
+		struct timeval expiration_time, const char *path_info, const char *user_agent,
+		struct ast_sip_endpoint *endpoint)
 {
 	char name[MAX_OBJECT_FIELD * 2 + 3];
 	RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup);
+	char hash[33];
 
-	snprintf(name, sizeof(name), "%s;@%s", ast_sorcery_object_get_id(aor), uri);
+	ast_md5_hash(hash, uri);
+	snprintf(name, sizeof(name), "%s;@%s", ast_sorcery_object_get_id(aor), hash);
 
 	if (!(contact = ast_sorcery_alloc(ast_sip_get_sorcery(), "contact", name))) {
 		return -1;
@@ -192,6 +296,7 @@ int ast_sip_location_add_contact(struct ast_sip_aor *aor, const char *uri,
 	ast_string_field_set(contact, uri, uri);
 	contact->expiration_time = expiration_time;
 	contact->qualify_frequency = aor->qualify_frequency;
+	contact->qualify_timeout = aor->qualify_timeout;
 	contact->authenticate_qualify = aor->authenticate_qualify;
 	if (path_info && aor->support_path) {
 		ast_string_field_set(contact, path, path_info);
@@ -205,6 +310,8 @@ int ast_sip_location_add_contact(struct ast_sip_aor *aor, const char *uri,
 		ast_string_field_set(contact, user_agent, user_agent);
 	}
 
+	contact->endpoint = ao2_bump(endpoint);
+
 	return ast_sorcery_create(ast_sip_get_sorcery(), contact);
 }
 
@@ -232,29 +339,6 @@ static int expiration_struct2str(const void *obj, const intptr_t *args, char **b
 	return (ast_asprintf(buf, "%ld", contact->expiration_time.tv_sec) < 0) ? -1 : 0;
 }
 
-/*! \brief Helper function which validates a permanent contact */
-static int permanent_contact_validate(void *data)
-{
-	const char *value = data;
-	pj_pool_t *pool;
-	pj_str_t contact_uri;
-	static const pj_str_t HCONTACT = { "Contact", 7 };
-
-	pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Permanent Contact Validation", 256, 256);
-	if (!pool) {
-		return -1;
-	}
-
-	pj_strdup2_with_null(pool, &contact_uri, value);
-	if (!pjsip_parse_hdr(pool, &HCONTACT, contact_uri.ptr, contact_uri.slen, NULL)) {
-		pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
-		return -1;
-	}
-
-	pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
-	return 0;
-}
-
 static int permanent_uri_sort_fn(const void *obj_left, const void *obj_right, int flags)
 {
 	const struct ast_sip_contact *object_left = obj_left;
@@ -300,13 +384,9 @@ static int permanent_uri_handler(const struct aco_option *opt, struct ast_variab
 	contacts = ast_strdupa(var->value);
 	while ((contact_uri = strsep(&contacts, ","))) {
 		struct ast_sip_contact *contact;
-		char contact_id[strlen(aor_id) + strlen(contact_uri) + 2 + 1];
-
-		if (ast_sip_push_task_synchronous(NULL, permanent_contact_validate, contact_uri)) {
-			ast_log(LOG_ERROR, "Permanent URI on aor '%s' with contact '%s' failed to parse\n",
-				ast_sorcery_object_get_id(aor), contact_uri);
-			return -1;
-		}
+		struct ast_sip_contact_status *status;
+		char hash[33];
+		char contact_id[strlen(aor_id) + sizeof(hash) + 2];
 
 		if (!aor->permanent_contacts) {
 			aor->permanent_contacts = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK,
@@ -316,13 +396,22 @@ static int permanent_uri_handler(const struct aco_option *opt, struct ast_variab
 			}
 		}
 
-		snprintf(contact_id, sizeof(contact_id), "%s@@%s", aor_id, contact_uri);
+		ast_md5_hash(hash, contact_uri);
+		snprintf(contact_id, sizeof(contact_id), "%s@@%s", aor_id, hash);
 		contact = ast_sorcery_alloc(ast_sip_get_sorcery(), "contact", contact_id);
 		if (!contact) {
 			return -1;
 		}
 
 		ast_string_field_set(contact, uri, contact_uri);
+
+		status = ast_res_pjsip_find_or_create_contact_status(contact);
+		if (!status) {
+			ao2_ref(contact, -1);
+			return -1;
+		}
+		ao2_ref(status, -1);
+
 		ao2_link(aor->permanent_contacts, contact);
 		ao2_ref(contact, -1);
 	}
@@ -528,13 +617,12 @@ struct ast_sip_endpoint_formatter endpoint_aor_formatter = {
 	.format_ami = format_ami_endpoint_aor
 };
 
-static struct ao2_container *cli_aor_get_container(void)
+static struct ao2_container *cli_aor_get_container(const char *regex)
 {
 	RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup);
 	struct ao2_container *s_container;
 
-	container = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "aor",
-		AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+	container = ast_sorcery_retrieve_by_regex(ast_sip_get_sorcery(), "aor", regex);
 	if (!container) {
 		return NULL;
 	}
@@ -627,59 +715,71 @@ static int cli_contact_compare(void *obj, void *arg, int flags)
 	return cmp;
 }
 
-static int cli_contact_hash(const void *obj, int flags)
+static int cli_contact_iterate(void *container, ao2_callback_fn callback, void *args)
 {
-	const struct ast_sip_contact_wrapper *wrapper = obj;
-	if (flags & OBJ_SEARCH_OBJECT) {
-		return ast_str_hash(wrapper->contact_id);
-	} else if (flags & OBJ_SEARCH_KEY) {
-		return ast_str_hash(obj);
-	}
-
-	return -1;
+	return ast_sip_for_each_contact(container, callback, args);
 }
 
-static int cli_contact_iterate(void *container, ao2_callback_fn callback, void *args)
+static int cli_filter_contacts(void *obj, void *arg, int flags)
 {
-	return ast_sip_for_each_contact(container, callback, args);
+	struct ast_sip_contact_wrapper *wrapper = obj;
+	regex_t *regexbuf = arg;
+
+	if (!regexec(regexbuf, wrapper->contact_id, 0, NULL, 0)) {
+		return 0;
+	}
+
+	return CMP_MATCH;
 }
 
-static struct ao2_container *cli_contact_get_container(void)
+static struct ao2_container *cli_contact_get_container(const char *regex)
 {
 	RAII_VAR(struct ao2_container *, parent_container, NULL, ao2_cleanup);
 	struct ao2_container *child_container;
+	regex_t regexbuf;
 
-	parent_container =  cli_aor_get_container();
+	parent_container =  cli_aor_get_container("");
 	if (!parent_container) {
 		return NULL;
 	}
 
-	child_container = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, 17,
-		cli_contact_hash, cli_contact_sort, cli_contact_compare);
+	child_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0,
+		cli_contact_sort, cli_contact_compare);
 	if (!child_container) {
 		return NULL;
 	}
 
 	ao2_callback(parent_container, OBJ_NODATA, cli_aor_gather_contacts, child_container);
 
+	if (!ast_strlen_zero(regex)) {
+		if (regcomp(&regexbuf, regex, REG_EXTENDED | REG_NOSUB)) {
+			ao2_ref(child_container, -1);
+			return NULL;
+		}
+		ao2_callback(child_container, OBJ_UNLINK | OBJ_MULTIPLE | OBJ_NODATA, cli_filter_contacts, &regexbuf);
+		regfree(&regexbuf);
+	}
+
 	return child_container;
 }
 
 static void *cli_contact_retrieve_by_id(const char *id)
 {
-	return ao2_find(cli_contact_get_container(), id, OBJ_KEY | OBJ_NOLOCK);
+	RAII_VAR(struct ao2_container *, container, cli_contact_get_container(""), ao2_cleanup);
+
+	return ao2_find(container, id, OBJ_KEY | OBJ_NOLOCK);
 }
 
 static int cli_contact_print_header(void *obj, void *arg, int flags)
 {
 	struct ast_sip_cli_context *context = arg;
 	int indent = CLI_INDENT_TO_SPACES(context->indent_level);
-	int filler = CLI_LAST_TABSTOP - indent - 18;
+	int filler = CLI_LAST_TABSTOP - indent - 23;
 
 	ast_assert(context->output_buffer != NULL);
 
 	ast_str_append(&context->output_buffer, 0,
-		"%*s:  <Aor/ContactUri%*.*s>  <Status....>  <RTT(ms)..>\n",
+		"%*s:  <Aor/ContactUri%*.*s> <Hash....> <Status> <RTT(ms)..>\n",
 		indent, "Contact", filler, filler, CLI_HEADER_FILLER);
 
 	return 0;
@@ -692,24 +792,28 @@ static int cli_contact_print_body(void *obj, void *arg, int flags)
 	struct ast_sip_cli_context *context = arg;
 	int indent;
 	int flexwidth;
+	const char *contact_id = ast_sorcery_object_get_id(contact);
+	const char *hash_start = contact_id + strlen(contact->aor) + 2;
 
 	RAII_VAR(struct ast_sip_contact_status *, status,
-		ast_sorcery_retrieve_by_id( ast_sip_get_sorcery(), CONTACT_STATUS, ast_sorcery_object_get_id(contact)),
+		ast_sorcery_retrieve_by_id( ast_sip_get_sorcery(), CONTACT_STATUS, contact_id),
 		ao2_cleanup);
 
 	ast_assert(contact->uri != NULL);
 	ast_assert(context->output_buffer != NULL);
 
 	indent = CLI_INDENT_TO_SPACES(context->indent_level);
-	flexwidth = CLI_LAST_TABSTOP - indent - 2;
+	flexwidth = CLI_LAST_TABSTOP - indent - 9 - strlen(contact->aor) + 1;
 
-	ast_str_append(&context->output_buffer, 0, "%*s:  %-*.*s  %-12.12s  %11.3f\n",
+	ast_str_append(&context->output_buffer, 0, "%*s:  %s/%-*.*s %-10.10s %-7.7s %11.3f\n",
 		indent,
 		"Contact",
+		contact->aor,
 		flexwidth, flexwidth,
-		wrapper->contact_id,
-		(status ? (status->status == AVAILABLE ? "Avail" : "Unavail") : "Unknown"),
-		(status ? ((long long) status->rtt) / 1000.0 : NAN));
+		contact->uri,
+		hash_start,
+		ast_sip_get_contact_short_status_label(status ? status->status : UNKNOWN),
+		(status && (status->status != UNKNOWN) ? ((long long) status->rtt) / 1000.0 : NAN));
 
 	return 0;
 }
@@ -804,12 +908,14 @@ static int cli_aor_print_body(void *obj, void *arg, int flags)
 static struct ast_cli_entry cli_commands[] = {
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "List PJSIP Aors",
 		.command = "pjsip list aors",
-		.usage = "Usage: pjsip list aors\n"
-				 "       List the configured PJSIP Aors\n"),
+		.usage = "Usage: pjsip list aors [ like <pattern> ]\n"
+				"       List the configured PJSIP Aors\n"
+				"       Optional regular expression pattern is used to filter the list.\n"),
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Aors",
 		.command = "pjsip show aors",
-		.usage = "Usage: pjsip show aors\n"
-				 "       Show the configured PJSIP Aors\n"),
+		.usage = "Usage: pjsip show aors [ like <pattern> ]\n"
+				"       Show the configured PJSIP Aors\n"
+				"       Optional regular expression pattern is used to filter the list.\n"),
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Aor",
 		.command = "pjsip show aor",
 		.usage = "Usage: pjsip show aor <id>\n"
@@ -817,12 +923,14 @@ static struct ast_cli_entry cli_commands[] = {
 
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "List PJSIP Contacts",
 		.command = "pjsip list contacts",
-		.usage = "Usage: pjsip list contacts\n"
-				 "       List the configured PJSIP contacts\n"),
+		.usage = "Usage: pjsip list contacts [ like <pattern> ]\n"
+				"       List the configured PJSIP contacts\n"
+				"       Optional regular expression pattern is used to filter the list.\n"),
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Contacts",
 		.command = "pjsip show contacts",
-		.usage = "Usage: pjsip show contacts\n"
-				 "       Show the configured PJSIP contacts\n"),
+		.usage = "Usage: pjsip show contacts [ like <pattern> ]\n"
+				"       Show the configured PJSIP contacts\n"
+				"       Optional regular expression pattern is used to filter the list.\n"),
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Contact",
 		.command = "pjsip show contact",
 		.usage = "Usage: pjsip show contact\n"
@@ -832,24 +940,41 @@ static struct ast_cli_entry cli_commands[] = {
 struct ast_sip_cli_formatter_entry *contact_formatter;
 struct ast_sip_cli_formatter_entry *aor_formatter;
 
+/*! \brief Always create a contact_status for each contact */
+static int contact_apply_handler(const struct ast_sorcery *sorcery, void *object)
+{
+	struct ast_sip_contact_status *status;
+	struct ast_sip_contact *contact = object;
+
+	status = ast_res_pjsip_find_or_create_contact_status(contact);
+	ao2_cleanup(status);
+
+	return status ? 0 : -1;
+}
+
 /*! \brief Initialize sorcery with location support */
 int ast_sip_initialize_sorcery_location(void)
 {
 	struct ast_sorcery *sorcery = ast_sip_get_sorcery();
+	int i;
+
 	ast_sorcery_apply_default(sorcery, "contact", "astdb", "registrar");
 	ast_sorcery_apply_default(sorcery, "aor", "config", "pjsip.conf,criteria=type=aor");
 
-	if (ast_sorcery_object_register(sorcery, "contact", contact_alloc, NULL, NULL) ||
+	if (ast_sorcery_object_register(sorcery, "contact", contact_alloc, NULL, contact_apply_handler) ||
 		ast_sorcery_object_register(sorcery, "aor", aor_alloc, NULL, NULL)) {
 		return -1;
 	}
 
+	ast_sorcery_observer_add(sorcery, "aor", &aor_observer);
+
 	ast_sorcery_object_field_register(sorcery, "contact", "type", "", OPT_NOOP_T, 0, 0);
 	ast_sorcery_object_field_register(sorcery, "contact", "uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, uri));
 	ast_sorcery_object_field_register(sorcery, "contact", "path", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, path));
 	ast_sorcery_object_field_register_custom(sorcery, "contact", "expiration_time", "", expiration_str2struct, expiration_struct2str, NULL, 0, 0);
 	ast_sorcery_object_field_register(sorcery, "contact", "qualify_frequency", 0, OPT_UINT_T,
-					  PARSE_IN_RANGE, FLDSET(struct ast_sip_contact, qualify_frequency), 0, 86400);
+		PARSE_IN_RANGE, FLDSET(struct ast_sip_contact, qualify_frequency), 0, 86400);
+	ast_sorcery_object_field_register(sorcery, "contact", "qualify_timeout", "3.0", OPT_DOUBLE_T, 0, FLDSET(struct ast_sip_contact, qualify_timeout));
 	ast_sorcery_object_field_register(sorcery, "contact", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, outbound_proxy));
 	ast_sorcery_object_field_register(sorcery, "contact", "user_agent", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, user_agent));
 
@@ -858,6 +983,7 @@ int ast_sip_initialize_sorcery_location(void)
 	ast_sorcery_object_field_register(sorcery, "aor", "maximum_expiration", "7200", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, maximum_expiration));
 	ast_sorcery_object_field_register(sorcery, "aor", "default_expiration", "3600", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, default_expiration));
 	ast_sorcery_object_field_register(sorcery, "aor", "qualify_frequency", 0, OPT_UINT_T, PARSE_IN_RANGE, FLDSET(struct ast_sip_aor, qualify_frequency), 0, 86400);
+	ast_sorcery_object_field_register(sorcery, "aor", "qualify_timeout", "3.0", OPT_DOUBLE_T, 0, FLDSET(struct ast_sip_aor, qualify_timeout));
 	ast_sorcery_object_field_register(sorcery, "aor", "authenticate_qualify", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_aor, authenticate_qualify));
 	ast_sorcery_object_field_register(sorcery, "aor", "max_contacts", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, max_contacts));
 	ast_sorcery_object_field_register(sorcery, "aor", "remove_existing", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_aor, remove_existing));
@@ -866,7 +992,7 @@ int ast_sip_initialize_sorcery_location(void)
 	ast_sorcery_object_field_register(sorcery, "aor", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_aor, outbound_proxy));
 	ast_sorcery_object_field_register(sorcery, "aor", "support_path", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_aor, support_path));
 
-	ast_sip_register_endpoint_formatter(&endpoint_aor_formatter);
+	internal_sip_register_endpoint_formatter(&endpoint_aor_formatter);
 
 	contact_formatter = ao2_alloc(sizeof(struct ast_sip_cli_formatter_entry), NULL);
 	if (!contact_formatter) {
@@ -898,15 +1024,27 @@ int ast_sip_initialize_sorcery_location(void)
 	ast_sip_register_cli_formatter(aor_formatter);
 	ast_cli_register_multiple(cli_commands, ARRAY_LEN(cli_commands));
 
+	/*
+	 * Reset StatsD gauges in case we didn't shut down cleanly.
+	 * Note that this must done here, as contacts will create the contact_status
+	 * object before PJSIP options handling is initialized.
+	 */
+	for (i = 0; i <= REMOVED; i++) {
+		ast_statsd_log_full_va("PJSIP.contacts.states.%s", AST_STATSD_GAUGE, 0, 1.0, ast_sip_get_contact_status_label(i));
+	}
+
 	return 0;
 }
 
 int ast_sip_destroy_sorcery_location(void)
 {
+	ast_sorcery_observer_remove(ast_sip_get_sorcery(), "aor", &aor_observer);
 	ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands));
 	ast_sip_unregister_cli_formatter(contact_formatter);
 	ast_sip_unregister_cli_formatter(aor_formatter);
 
+	internal_sip_unregister_endpoint_formatter(&endpoint_aor_formatter);
+
 	return 0;
 }
 
diff --git a/res/res_pjsip/pjsip_cli.c b/res/res_pjsip/pjsip_cli.c
index e15e75d..bbd0ac4 100644
--- a/res/res_pjsip/pjsip_cli.c
+++ b/res/res_pjsip/pjsip_cli.c
@@ -125,6 +125,7 @@ char *ast_sip_cli_traverse_objects(struct ast_cli_entry *e, int cmd, struct ast_
 	const char *cmd2;
 	const char *object_id;
 	char formatter_type[64];
+	const char *regex;
 
 	struct ast_sip_cli_context context = {
 		.indent_level = 0,
@@ -162,6 +163,18 @@ char *ast_sip_cli_traverse_objects(struct ast_cli_entry *e, int cmd, struct ast_
 		is_container = 1;
 	}
 
+	if (cmd != CLI_GENERATE
+		&& is_container
+		&& a->argc >= 4
+		&& strcmp(object_id, "like") == 0) {
+		if (ast_strlen_zero(a->argv[4])) {
+			return CLI_SHOWUSAGE;
+		}
+		regex = a->argv[4];
+	} else {
+		regex = "";
+	}
+
 	if (cmd == CLI_GENERATE
 		&& (is_container
 			|| a->argc > 4
@@ -187,7 +200,7 @@ char *ast_sip_cli_traverse_objects(struct ast_cli_entry *e, int cmd, struct ast_
 		" =========================================================================================\n\n");
 
 	if (is_container || cmd == CLI_GENERATE) {
-		container = formatter_entry->get_container();
+		container = formatter_entry->get_container(regex);
 		if (!container) {
 			ast_cli(a->fd, "No container returned for object type %s.\n",
 				formatter_type);
@@ -328,6 +341,28 @@ int ast_sip_unregister_cli_formatter(struct ast_sip_cli_formatter_entry *formatt
 	return 0;
 }
 
+static char *handle_pjsip_show_version(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	switch(cmd) {
+	case CLI_INIT:
+		e->command = "pjsip show version";
+		e->usage =
+			"Usage: pjsip show version\n"
+			"       Show the version of pjproject that res_pjsip is running against\n";
+		return NULL;
+	case CLI_GENERATE:
+		return NULL;
+	}
+
+	ast_cli(a->fd, "PJPROJECT version currently running against: %s\n", pj_get_version());
+
+	return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry pjsip_cli[] = {
+	AST_CLI_DEFINE(handle_pjsip_show_version, "Show the version of pjproject in use"),
+};
+
 int ast_sip_initialize_cli(void)
 {
 	formatter_registry = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, 17,
@@ -338,10 +373,13 @@ int ast_sip_initialize_cli(void)
 		return -1;
 	}
 
+	ast_cli_register_multiple(pjsip_cli, ARRAY_LEN(pjsip_cli));
+
 	return 0;
 }
 
 void ast_sip_destroy_cli(void)
 {
+	ast_cli_unregister_multiple(pjsip_cli, ARRAY_LEN(pjsip_cli));
 	ao2_ref(formatter_registry, -1);
 }
diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c
index 63e63fb..746a457 100644
--- a/res/res_pjsip/pjsip_configuration.c
+++ b/res/res_pjsip/pjsip_configuration.c
@@ -19,6 +19,8 @@
 #include "asterisk/utils.h"
 #include "asterisk/sorcery.h"
 #include "asterisk/callerid.h"
+#include "asterisk/test.h"
+#include "asterisk/statsd.h"
 
 /*! \brief Number of buckets for persistent endpoint information */
 #define PERSISTENT_BUCKETS 53
@@ -59,45 +61,186 @@ static int persistent_endpoint_cmp(void *obj, void *arg, int flags)
 static int persistent_endpoint_update_state(void *obj, void *arg, int flags)
 {
 	struct sip_persistent_endpoint *persistent = obj;
-	char *aor = arg;
-	RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup);
-	RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
+	struct ast_endpoint *endpoint = persistent->endpoint;
+	struct ast_sip_contact_status *status = arg;
+	struct ao2_container *contacts;
+	struct ast_json *blob;
+	struct ao2_iterator i;
+	struct ast_sip_contact *contact;
+	enum ast_endpoint_state state = AST_ENDPOINT_OFFLINE;
+
+	if (status) {
+		char rtt[32];
+
+		/* If the status' aor isn't one of the endpoint's, we skip */
+		if (!strstr(persistent->aors, status->aor)) {
+			return 0;
+		}
 
-	if (!ast_strlen_zero(aor) && !strstr(persistent->aors, aor)) {
+		snprintf(rtt, sizeof(rtt), "%" PRId64, status->rtt);
+		blob = ast_json_pack("{s: s, s: s, s: s, s: s, s: s}",
+			"contact_status", ast_sip_get_contact_status_label(status->status),
+			"aor", status->aor,
+			"uri", status->uri,
+			"roundtrip_usec", rtt,
+			"endpoint_name", ast_endpoint_get_resource(endpoint));
+		ast_endpoint_blob_publish(endpoint, ast_endpoint_contact_state_type(), blob);
+		ast_json_unref(blob);
+	}
+
+	/* Find all the contacts for this endpoint.  If ANY are available,
+	 * mark the endpoint as ONLINE.
+	 */
+	contacts = ast_sip_location_retrieve_contacts_from_aor_list(persistent->aors);
+	if (contacts) {
+		i = ao2_iterator_init(contacts, 0);
+		while (state == AST_ENDPOINT_OFFLINE && (contact = ao2_iterator_next(&i))) {
+			struct ast_sip_contact_status *contact_status;
+			const char *contact_id = ast_sorcery_object_get_id(contact);
+
+			contact_status = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(),
+				CONTACT_STATUS, contact_id);
+
+			if (contact_status && contact_status->status != UNAVAILABLE) {
+				state = AST_ENDPOINT_ONLINE;
+			}
+			ao2_cleanup(contact_status);
+			ao2_ref(contact, -1);
+		}
+		ao2_iterator_destroy(&i);
+		ao2_ref(contacts, -1);
+	}
+
+	/* If there was no state change, don't publish anything. */
+	if (ast_endpoint_get_state(endpoint) == state) {
 		return 0;
 	}
 
-	if ((contact = ast_sip_location_retrieve_contact_from_aor_list(persistent->aors))) {
-		ast_endpoint_set_state(persistent->endpoint, AST_ENDPOINT_ONLINE);
+	if (state == AST_ENDPOINT_ONLINE) {
+		ast_endpoint_set_state(endpoint, AST_ENDPOINT_ONLINE);
 		blob = ast_json_pack("{s: s}", "peer_status", "Reachable");
+		ast_verb(1, "Endpoint %s is now Reachable\n", ast_endpoint_get_resource(endpoint));
 	} else {
-		ast_endpoint_set_state(persistent->endpoint, AST_ENDPOINT_OFFLINE);
+		ast_endpoint_set_state(endpoint, AST_ENDPOINT_OFFLINE);
 		blob = ast_json_pack("{s: s}", "peer_status", "Unreachable");
+		ast_verb(1, "Endpoint %s is now Unreachable\n", ast_endpoint_get_resource(endpoint));
 	}
 
-	ast_endpoint_blob_publish(persistent->endpoint, ast_endpoint_state_type(), blob);
-
-	ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "PJSIP/%s", ast_endpoint_get_resource(persistent->endpoint));
+	ast_endpoint_blob_publish(endpoint, ast_endpoint_state_type(), blob);
+	ast_json_unref(blob);
+	ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "PJSIP/%s", ast_endpoint_get_resource(endpoint));
 
 	return 0;
 }
 
-/*! \brief Function called when stuff relating to a contact happens (created/deleted) */
-static void persistent_endpoint_contact_observer(const void *object)
+/*! \brief Function called when a contact is created */
+static void persistent_endpoint_contact_created_observer(const void *object)
+{
+	const struct ast_sip_contact *contact = object;
+	struct ast_sip_contact_status *contact_status;
+
+	contact_status = ast_sorcery_alloc(ast_sip_get_sorcery(), CONTACT_STATUS,
+		ast_sorcery_object_get_id(contact));
+	if (!contact_status) {
+		ast_log(LOG_ERROR, "Unable to create ast_sip_contact_status for contact %s/%s\n",
+			contact->aor, contact->uri);
+		return;
+	}
+	contact_status->uri = ast_strdup(contact->uri);
+	if (!contact_status->uri) {
+		ao2_cleanup(contact_status);
+		return;
+	}
+	contact_status->status = CREATED;
+
+	ast_verb(1, "Contact %s/%s has been created\n",contact->aor, contact->uri);
+
+	ao2_callback(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, contact_status);
+	ao2_cleanup(contact_status);
+}
+
+/*! \brief Function called when a contact is deleted */
+static void persistent_endpoint_contact_deleted_observer(const void *object)
 {
-	char *id = ast_strdupa(ast_sorcery_object_get_id(object)), *aor = NULL;
+	const struct ast_sip_contact *contact = object;
+	struct ast_sip_contact_status *contact_status;
 
-	aor = strsep(&id, ";@");
+	contact_status = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), CONTACT_STATUS, ast_sorcery_object_get_id(contact));
+	if (!contact_status) {
+		ast_log(LOG_ERROR, "Unable to create ast_sip_contact_status for contact %s/%s\n",
+			contact->aor, contact->uri);
+		return;
+	}
+
+	ast_verb(1, "Contact %s/%s has been deleted\n", contact->aor, contact->uri);
+	ast_statsd_log_string_va("PJSIP.contacts.states.%s", AST_STATSD_GAUGE,
+		"-1", 1.0, ast_sip_get_contact_status_label(contact_status->status));
+	ast_statsd_log_string_va("PJSIP.contacts.states.%s", AST_STATSD_GAUGE,
+		"+1", 1.0, ast_sip_get_contact_status_label(REMOVED));
 
-	ao2_callback(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, aor);
+	ao2_callback(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, contact_status);
+	ast_sorcery_delete(ast_sip_get_sorcery(), contact_status);
+	ao2_cleanup(contact_status);
 }
 
 /*! \brief Observer for contacts so state can be updated on respective endpoints */
 static const struct ast_sorcery_observer state_contact_observer = {
-	.created = persistent_endpoint_contact_observer,
-	.deleted = persistent_endpoint_contact_observer,
+	.created = persistent_endpoint_contact_created_observer,
+	.deleted = persistent_endpoint_contact_deleted_observer,
 };
 
+/*! \brief Function called when a contact_status is updated */
+static void persistent_endpoint_contact_status_observer(const void *object)
+{
+	struct ast_sip_contact_status *contact_status = (struct ast_sip_contact_status *)object;
+
+	/* If rtt_start is set (this is the outgoing OPTIONS), ignore. */
+	if (contact_status->rtt_start.tv_sec > 0) {
+		return;
+	}
+
+	if (contact_status->status != contact_status->last_status) {
+		ast_verb(1, "Contact %s/%s is now %s.  RTT: %.3f msec\n", contact_status->aor, contact_status->uri,
+			ast_sip_get_contact_status_label(contact_status->status),
+			contact_status->rtt / 1000.0);
+
+		ast_statsd_log_string_va("PJSIP.contacts.states.%s", AST_STATSD_GAUGE,
+			"-1", 1.0, ast_sip_get_contact_status_label(contact_status->last_status));
+		ast_statsd_log_string_va("PJSIP.contacts.states.%s", AST_STATSD_GAUGE,
+			"+1", 1.0, ast_sip_get_contact_status_label(contact_status->status));
+
+		ast_test_suite_event_notify("AOR_CONTACT_UPDATE",
+			"Contact: %s\r\n"
+				"Status: %s",
+			ast_sorcery_object_get_id(contact_status),
+			ast_sip_get_contact_status_label(contact_status->status));
+
+		ao2_callback(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, contact_status);
+	} else {
+		ast_debug(3, "Contact %s/%s status didn't change: %s, RTT: %.3f msec\n",
+			contact_status->aor, contact_status->uri, ast_sip_get_contact_status_label(contact_status->status),
+			contact_status->rtt / 1000.0);
+	}
+
+	ast_statsd_log_full_va("PJSIP.contacts.%s.rtt", AST_STATSD_TIMER,
+		contact_status->status != AVAILABLE ? -1 : contact_status->rtt / 1000, 1.0, ast_sorcery_object_get_id(contact_status));
+}
+
+/*! \brief Observer for contacts so state can be updated on respective endpoints */
+static const struct ast_sorcery_observer state_contact_status_observer = {
+	.updated = persistent_endpoint_contact_status_observer,
+};
+
+static void endpoint_deleted_observer(const void *object)
+{
+	const struct ast_sip_endpoint *endpoint = object;
+
+	ao2_find(persistent_endpoints, ast_endpoint_get_resource(endpoint->persistent), OBJ_SEARCH_KEY | OBJ_UNLINK | OBJ_NODATA);
+}
+
+static const struct ast_sorcery_observer endpoint_observers = {
+	.deleted = endpoint_deleted_observer,
+};
 
 static int dtmf_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
 {
@@ -109,6 +252,8 @@ static int dtmf_handler(const struct aco_option *opt, struct ast_variable *var,
 		endpoint->dtmf = AST_SIP_DTMF_INBAND;
 	} 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 {
@@ -129,6 +274,8 @@ static int dtmf_to_str(const void *obj, const intptr_t *args, char **buf)
 		*buf = "inband"; break;
 	case AST_SIP_DTMF_INFO :
 		*buf = "info"; break;
+       case AST_SIP_DTMF_AUTO :
+                *buf = "auto"; break;
 	default:
 		*buf = "none";
 	}
@@ -141,13 +288,14 @@ static int prack_handler(const struct aco_option *opt, struct ast_variable *var,
 {
 	struct ast_sip_endpoint *endpoint = obj;
 
+	/* clear all */
+	endpoint->extensions.flags &= ~(PJSIP_INV_SUPPORT_100REL | PJSIP_INV_REQUIRE_100REL);
+
 	if (ast_true(var->value)) {
 		endpoint->extensions.flags |= PJSIP_INV_SUPPORT_100REL;
-	} else if (ast_false(var->value)) {
-		endpoint->extensions.flags &= PJSIP_INV_SUPPORT_100REL;
 	} else if (!strcasecmp(var->value, "required")) {
 		endpoint->extensions.flags |= PJSIP_INV_REQUIRE_100REL;
-	} else {
+	} else if (!ast_false(var->value)){
 		return -1;
 	}
 
@@ -174,15 +322,18 @@ static int timers_handler(const struct aco_option *opt, struct ast_variable *var
 {
 	struct ast_sip_endpoint *endpoint = obj;
 
+	/* clear all */
+	endpoint->extensions.flags &= ~(PJSIP_INV_SUPPORT_TIMER | PJSIP_INV_REQUIRE_TIMER
+					| PJSIP_INV_ALWAYS_USE_TIMER);
+
+	/* set only the specified flag and let pjsip normalize if needed */
 	if (ast_true(var->value)) {
 		endpoint->extensions.flags |= PJSIP_INV_SUPPORT_TIMER;
-	} else if (ast_false(var->value)) {
-		endpoint->extensions.flags &= PJSIP_INV_SUPPORT_TIMER;
 	} else if (!strcasecmp(var->value, "required")) {
 		endpoint->extensions.flags |= PJSIP_INV_REQUIRE_TIMER;
-	} else if (!strcasecmp(var->value, "always")) {
+	} else if (!strcasecmp(var->value, "always") || !strcasecmp(var->value, "forced")) {
 		endpoint->extensions.flags |= PJSIP_INV_ALWAYS_USE_TIMER;
-	} else {
+	} else if (!ast_false(var->value)) {
 		return -1;
 	}
 
@@ -240,6 +391,10 @@ int ast_sip_auth_vector_init(struct ast_sip_auth_vector *auths, const char *valu
 	}
 
 	while ((val = strsep(&auth_names, ","))) {
+		if (ast_strlen_zero(val)) {
+			continue;
+		}
+
 		val = ast_strdup(val);
 		if (!val) {
 			goto failure;
@@ -528,7 +683,7 @@ static int media_encryption_handler(const struct aco_option *opt, struct ast_var
 		endpoint->media.rtp.encryption = AST_SIP_MEDIA_ENCRYPT_SDES;
 	} else if (!strcasecmp("dtls", var->value)) {
 		endpoint->media.rtp.encryption = AST_SIP_MEDIA_ENCRYPT_DTLS;
-		ast_rtp_dtls_cfg_parse(&endpoint->media.rtp.dtls_cfg, "dtlsenable", "yes");
+		return ast_rtp_dtls_cfg_parse(&endpoint->media.rtp.dtls_cfg, "dtlsenable", "yes");
 	} else {
 		return -1;
 	}
@@ -538,7 +693,7 @@ static int media_encryption_handler(const struct aco_option *opt, struct ast_var
 
 static const char *media_encryption_map[] = {
 	[AST_SIP_MEDIA_TRANSPORT_INVALID] = "invalid",
-	[AST_SIP_MEDIA_ENCRYPT_NONE] = "none",
+	[AST_SIP_MEDIA_ENCRYPT_NONE] = "no",
 	[AST_SIP_MEDIA_ENCRYPT_SDES] = "sdes",
 	[AST_SIP_MEDIA_ENCRYPT_DTLS] = "dtls",
 };
@@ -646,15 +801,15 @@ static int dtls_handler(const struct aco_option *opt,
 {
 	struct ast_sip_endpoint *endpoint = obj;
 	char *name = ast_strdupa(var->name);
-	char *front, *buf = name;
+	char *front, *back, *buf = name;
 
 	/* strip out underscores in the name */
-	front = strtok(buf, "_");
+	front = strtok_r(buf, "_", &back);
 	while (front) {
 		int size = strlen(front);
 		ast_copy_string(buf, front, size + 1);
 		buf += size;
-		front = strtok(NULL, "_");
+		front = strtok_r(NULL, "_", &back);
 	}
 
 	return ast_rtp_dtls_cfg_parse(&endpoint->media.rtp.dtls_cfg, name, var->value);
@@ -918,29 +1073,6 @@ static struct ast_endpoint *persistent_endpoint_find_or_create(const struct ast_
 	return persistent->endpoint;
 }
 
-/*! \brief Helper function which validates an outbound proxy */
-static int outbound_proxy_validate(void *data)
-{
-	const char *proxy = data;
-	pj_pool_t *pool;
-	pj_str_t tmp;
-	static const pj_str_t ROUTE_HNAME = { "Route", 5 };
-
-	pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Outbound Proxy Validation", 256, 256);
-	if (!pool) {
-		return -1;
-	}
-
-	pj_strdup2_with_null(pool, &tmp, proxy);
-	if (!pjsip_parse_hdr(pool, &ROUTE_HNAME, tmp.ptr, tmp.slen, NULL)) {
-		pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
-		return -1;
-	}
-
-	pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
-	return 0;
-}
-
 /*! \brief Callback function for when an object is finalized */
 static int sip_endpoint_apply_handler(const struct ast_sorcery *sorcery, void *obj)
 {
@@ -950,12 +1082,7 @@ static int sip_endpoint_apply_handler(const struct ast_sorcery *sorcery, void *o
 		return -1;
 	}
 
-	if (!ast_strlen_zero(endpoint->outbound_proxy) &&
-		ast_sip_push_task_synchronous(NULL, outbound_proxy_validate, (char*)endpoint->outbound_proxy)) {
-		ast_log(LOG_ERROR, "Invalid outbound proxy '%s' specified on endpoint '%s'\n",
-			endpoint->outbound_proxy, ast_sorcery_object_get_id(endpoint));
-		return -1;
-	} else if (endpoint->extensions.timer.min_se < 90) {
+	if (endpoint->extensions.timer.min_se < 90) {
 		ast_log(LOG_ERROR, "Session timer minimum expires time must be 90 or greater on endpoint '%s'\n",
 			ast_sorcery_object_get_id(endpoint));
 		return -1;
@@ -1159,7 +1286,7 @@ static int ami_show_endpoint(struct mansession *s, const struct message *m)
 		      ast_sip_get_sorcery(), "endpoint", endpoint_name))) {
 		astman_send_error_va(s, m, "Unable to retrieve endpoint %s\n",
 			endpoint_name);
-		return -1;
+		return 0;
 	}
 
 	astman_send_listack(s, m, "Following are Events for each object "
@@ -1172,12 +1299,8 @@ static int ami_show_endpoint(struct mansession *s, const struct message *m)
 			endpoint_name);
 	}
 
-	astman_append(s, "Event: EndpointDetailComplete\r\n");
-	if (!ast_strlen_zero(ami.action_id)) {
-		astman_append(s, "ActionID: %s\r\n", ami.action_id);
-	}
-	astman_append(s, "EventList: Complete\r\n"
-		      "ListItems: %d\r\n\r\n", ami.count + 1);
+	astman_send_list_complete_start(s, m, "EndpointDetailComplete", ami.count + 1);
+	astman_send_list_complete_end(s);
 
 	return 0;
 }
@@ -1203,7 +1326,7 @@ static int format_ami_endpoints(void *obj, void *arg, int flags)
 		 ast_sip_create_ami_event("EndpointList", ami), ast_free);
 
 	if (!buf) {
-		return -1;
+		return CMP_STOP;
 	}
 
 	sip_sorcery_object_ami_set_type_name(endpoint, &buf);
@@ -1242,7 +1365,8 @@ static int ami_show_endpoints(struct mansession *s, const struct message *m)
 
 	endpoints = ast_sip_get_endpoints();
 	if (!endpoints) {
-		return -1;
+		astman_send_error(s, m, "Could not get endpoints\n");
+		return 0;
 	}
 
 	if (!(num = ao2_container_count(endpoints))) {
@@ -1255,22 +1379,17 @@ static int ami_show_endpoints(struct mansession *s, const struct message *m)
 
 	ao2_callback(endpoints, OBJ_NODATA, format_ami_endpoints, &ami);
 
-	astman_append(s, "Event: EndpointListComplete\r\n");
-	if (!ast_strlen_zero(ami.action_id)) {
-		astman_append(s, "ActionID: %s\r\n", ami.action_id);
-	}
-	astman_append(s, "EventList: Complete\r\n"
-		      "ListItems: %d\r\n\r\n", num);
+	astman_send_list_complete_start(s, m, "EndpointListComplete", num);
+	astman_send_list_complete_end(s);
 	return 0;
 }
 
-static struct ao2_container *cli_endpoint_get_container(void)
+static struct ao2_container *cli_endpoint_get_container(const char *regex)
 {
 	RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup);
 	struct ao2_container *s_container;
 
-	container = ast_sorcery_retrieve_by_fields(sip_sorcery, "endpoint",
-		AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+	container = ast_sorcery_retrieve_by_regex(sip_sorcery, "endpoint", regex);
 	if (!container) {
 		return NULL;
 	}
@@ -1384,12 +1503,26 @@ static int cli_endpoint_gather_channels(void *obj, void *arg, int flags)
 	return 0;
 }
 
-static struct ao2_container *cli_channel_get_container(void)
+static int cli_filter_channels(void *obj, void *arg, int flags)
+{
+	struct ast_channel_snapshot *channel = obj;
+	regex_t *regexbuf = arg;
+
+	if (!regexec(regexbuf, channel->name, 0, NULL, 0)
+		|| !regexec(regexbuf, channel->appl, 0, NULL, 0)) {
+		return 0;
+	}
+
+	return CMP_MATCH;
+}
+
+static struct ao2_container *cli_channel_get_container(const char *regex)
 {
 	RAII_VAR(struct ao2_container *, parent_container, NULL, ao2_cleanup);
 	struct ao2_container *child_container;
+	regex_t regexbuf;
 
-	parent_container = cli_endpoint_get_container();
+	parent_container = cli_endpoint_get_container("");
 	if (!parent_container) {
 		return NULL;
 	}
@@ -1401,6 +1534,15 @@ static struct ao2_container *cli_channel_get_container(void)
 
 	ao2_callback(parent_container, OBJ_NODATA, cli_endpoint_gather_channels, child_container);
 
+	if (!ast_strlen_zero(regex)) {
+		if (regcomp(&regexbuf, regex, REG_EXTENDED | REG_NOSUB)) {
+			ao2_ref(child_container, -1);
+			return NULL;
+		}
+		ao2_callback(child_container, OBJ_UNLINK | OBJ_MULTIPLE | OBJ_NODATA, cli_filter_channels, &regexbuf);
+		regfree(&regexbuf);
+	}
+
 	return child_container;
 }
 
@@ -1413,7 +1555,7 @@ static const char *cli_channel_get_id(const void *obj)
 
 static void *cli_channel_retrieve_by_id(const char *id)
 {
-	RAII_VAR(struct ao2_container *, container, cli_channel_get_container(), ao2_cleanup);
+	RAII_VAR(struct ao2_container *, container, cli_channel_get_container(""), ao2_cleanup);
 
 	return ao2_find(container, id, OBJ_KEY | OBJ_NOLOCK);
 }
@@ -1615,12 +1757,14 @@ static int cli_endpoint_print_body(void *obj, void *arg, int flags)
 static struct ast_cli_entry cli_commands[] = {
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "List PJSIP Channels",
 		.command = "pjsip list channels",
-		.usage = "Usage: pjsip list channels\n"
-				 "       List the active PJSIP channels\n"),
+		.usage = "Usage: pjsip list channels [ like <pattern> ]\n"
+				"       List the active PJSIP channels\n"
+				"       Optional regular expression pattern is used to filter the list.\n"),
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Channels",
 		.command = "pjsip show channels",
-		.usage = "Usage: pjsip show channels\n"
-				 "       List(detailed) the active PJSIP channels\n"),
+		.usage = "Usage: pjsip show channels [ like <pattern> ]\n"
+				"       List(detailed) the active PJSIP channels\n"
+				"       Optional regular expression pattern is used to filter the list.\n"),
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Channel",
 		.command = "pjsip show channel",
 		.usage = "Usage: pjsip show channel\n"
@@ -1628,12 +1772,14 @@ static struct ast_cli_entry cli_commands[] = {
 
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "List PJSIP Endpoints",
 		.command = "pjsip list endpoints",
-		.usage = "Usage: pjsip list endpoints\n"
-				 "       List the configured PJSIP endpoints\n"),
+		.usage = "Usage: pjsip list endpoints [ like <pattern> ]\n"
+				"       List the configured PJSIP endpoints\n"
+				"       Optional regular expression pattern is used to filter the list.\n"),
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Endpoints",
 		.command = "pjsip show endpoints",
-		.usage = "Usage: pjsip show endpoints\n"
-				 "       List(detailed) the configured PJSIP endpoints\n"),
+		.usage = "Usage: pjsip show endpoints [ like <pattern> ]\n"
+				"       List(detailed) the configured PJSIP endpoints\n"
+				"       Optional regular expression pattern is used to filter the list.\n"),
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Endpoint",
 		.command = "pjsip show endpoint",
 		.usage = "Usage: pjsip show endpoint <id>\n"
@@ -1678,7 +1824,9 @@ int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_mod
 		return -1;
 	}
 
-	ast_sorcery_internal_object_register(sip_sorcery, "nat_hook", sip_nat_hook_alloc, NULL, NULL);
+	if (ast_sorcery_internal_object_register(sip_sorcery, "nat_hook", sip_nat_hook_alloc, NULL, NULL)) {
+		ast_log(LOG_ERROR, "Failed to register nat_hook\n");
+	}
 
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "type", "", OPT_NOOP_T, 0, 0);
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "context", "default", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, context));
@@ -1715,6 +1863,7 @@ int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_mod
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "trust_id_outbound", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, id.trust_outbound));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "send_pai", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, id.send_pai));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "send_rpid", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, id.send_rpid));
+	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rpid_immediate", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, rpid_immediate));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "send_diversion", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, id.send_diversion));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "mailboxes", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, subscription.mwi.mailboxes));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "aggregate_mwi", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, subscription.mwi.aggregate));
@@ -1722,6 +1871,9 @@ int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_mod
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "use_avpf", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtp.use_avpf));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "force_avp", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtp.force_avp));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "media_use_received_transport", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtp.use_received_transport));
+	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rtp_keepalive", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, media.rtp.keepalive));
+	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rtp_timeout", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, media.rtp.timeout));
+	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rtp_timeout_hold", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, media.rtp.timeout_hold));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "one_touch_recording", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, info.recording.enabled));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "inband_progress", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, inband_progress));
 	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "call_group", "", group_handler, callgroup_to_str, NULL, 0, 0);
@@ -1740,6 +1892,7 @@ int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_mod
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "record_on_feature", "automixmon", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, info.recording.onfeature));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "record_off_feature", "automixmon", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, info.recording.offfeature));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "allow_transfer", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, allowtransfer));
+	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "user_eq_phone", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, usereqphone));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "sdp_owner", "-", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, media.sdpowner));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "sdp_session", "Asterisk", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, media.sdpsession));
 	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "tos_audio", "0", tos_handler, tos_audio_to_str, NULL, 0, 0);
@@ -1763,10 +1916,11 @@ int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_mod
 	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_fingerprint", "", dtls_handler, dtlsfingerprint_to_str, NULL, 0, 0);
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "srtp_tag_32", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtp.srtp_tag_32));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "media_encryption_optimistic", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtp.encryption_optimistic));
+	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "g726_non_standard", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.g726_non_standard));
 	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "redirect_method", "user", redirect_handler, NULL, NULL, 0, 0);
 	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "set_var", "", set_var_handler, set_var_to_str, set_var_to_vl, 0, 0);
-	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "message_context", "", OPT_STRINGFIELD_T, 1, STRFLDSET(struct ast_sip_endpoint, message_context));
-	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "accountcode", "", OPT_STRINGFIELD_T, 1, STRFLDSET(struct ast_sip_endpoint, accountcode));
+	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "message_context", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, message_context));
+	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "accountcode", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, accountcode));
 
 	if (ast_sip_initialize_sorcery_transport()) {
 		ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n");
@@ -1789,7 +1943,9 @@ int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_mod
 		return -1;
 	}
 
+	ast_sorcery_observer_add(sip_sorcery, "endpoint", &endpoint_observers);
 	ast_sorcery_observer_add(sip_sorcery, "contact", &state_contact_observer);
+	ast_sorcery_observer_add(sip_sorcery, CONTACT_STATUS, &state_contact_status_observer);
 
 	if (ast_sip_initialize_sorcery_domain_alias()) {
 		ast_log(LOG_ERROR, "Failed to register SIP domain aliases support with sorcery\n");
@@ -1846,6 +2002,9 @@ int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_mod
 
 void ast_res_pjsip_destroy_configuration(void)
 {
+	ast_sorcery_observer_remove(sip_sorcery, CONTACT_STATUS, &state_contact_status_observer);
+	ast_sorcery_observer_remove(sip_sorcery, "contact", &state_contact_observer);
+	ast_sip_destroy_sorcery_global();
 	ast_sip_destroy_sorcery_location();
 	ast_sip_destroy_sorcery_auth();
 	ast_sip_destroy_sorcery_transport();
@@ -1855,6 +2014,7 @@ void ast_res_pjsip_destroy_configuration(void)
 	ast_sip_unregister_cli_formatter(endpoint_formatter);
 	ast_sip_unregister_cli_formatter(channel_formatter);
 	ast_sorcery_unref(sip_sorcery);
+	ao2_cleanup(persistent_endpoints);
 }
 
 int ast_res_pjsip_reload_configuration(void)
diff --git a/res/res_pjsip/pjsip_distributor.c b/res/res_pjsip/pjsip_distributor.c
index 90744fd..0e0e90f 100644
--- a/res/res_pjsip/pjsip_distributor.c
+++ b/res/res_pjsip/pjsip_distributor.c
@@ -21,22 +21,107 @@
 #include <pjsip.h>
 
 #include "asterisk/res_pjsip.h"
+#include "include/res_pjsip_private.h"
+#include "asterisk/taskprocessor.h"
+#include "asterisk/threadpool.h"
 
 static int distribute(void *data);
 static pj_bool_t distributor(pjsip_rx_data *rdata);
+static pj_status_t record_serializer(pjsip_tx_data *tdata);
 
 static pjsip_module distributor_mod = {
 	.name = {"Request Distributor", 19},
 	.priority = PJSIP_MOD_PRIORITY_TSX_LAYER - 6,
+	.on_tx_request = record_serializer,
 	.on_rx_request = distributor,
 	.on_rx_response = distributor,
 };
 
+/*!
+ * \internal
+ * \brief Record the task's serializer name on the tdata structure.
+ * \since 14.0.0
+ *
+ * \param tdata The outgoing message.
+ *
+ * \retval PJ_SUCCESS.
+ */
+static pj_status_t record_serializer(pjsip_tx_data *tdata)
+{
+	struct ast_taskprocessor *serializer;
+
+	serializer = ast_threadpool_serializer_get_current();
+	if (serializer) {
+		const char *name;
+
+		name = ast_taskprocessor_name(serializer);
+		if (!ast_strlen_zero(name)
+			&& (!tdata->mod_data[distributor_mod.id]
+				|| strcmp(tdata->mod_data[distributor_mod.id], name))) {
+			char *tdata_name;
+
+			/* The serializer in use changed. */
+			tdata_name = pj_pool_alloc(tdata->pool, strlen(name) + 1);
+			strcpy(tdata_name, name);/* Safe */
+
+			tdata->mod_data[distributor_mod.id] = tdata_name;
+		}
+	}
+
+	return PJ_SUCCESS;
+}
+
+/*!
+ * \internal
+ * \brief Find the request tdata to get the serializer it used.
+ * \since 14.0.0
+ *
+ * \param rdata The incoming message.
+ *
+ * \retval serializer on success.
+ * \retval NULL on error or could not find the serializer.
+ */
+static struct ast_taskprocessor *find_request_serializer(pjsip_rx_data *rdata)
+{
+	struct ast_taskprocessor *serializer = NULL;
+	pj_str_t tsx_key;
+	pjsip_transaction *tsx;
+
+	pjsip_tsx_create_key(rdata->tp_info.pool, &tsx_key, PJSIP_ROLE_UAC,
+		&rdata->msg_info.cseq->method, rdata);
+
+	tsx = pjsip_tsx_layer_find_tsx(&tsx_key, PJ_TRUE);
+	if (!tsx) {
+		ast_debug(1, "Could not find %.*s transaction for %d response.\n",
+			(int) pj_strlen(&rdata->msg_info.cseq->method.name),
+			pj_strbuf(&rdata->msg_info.cseq->method.name),
+			rdata->msg_info.msg->line.status.code);
+		return NULL;
+	}
+
+	if (tsx->last_tx) {
+		const char *serializer_name;
+
+		serializer_name = tsx->last_tx->mod_data[distributor_mod.id];
+		if (!ast_strlen_zero(serializer_name)) {
+			serializer = ast_taskprocessor_get(serializer_name, TPS_REF_IF_EXISTS);
+		}
+	}
+
+#ifdef HAVE_PJ_TRANSACTION_GRP_LOCK
+	pj_grp_lock_release(tsx->grp_lock);
+#else
+	pj_mutex_unlock(tsx->mutex);
+#endif
+
+	return serializer;
+}
+
 /*! Dialog-specific information the distributor uses */
 struct distributor_dialog_data {
-	/* Serializer to distribute tasks to for this dialog */
+	/*! Serializer to distribute tasks to for this dialog */
 	struct ast_taskprocessor *serializer;
-	/* Endpoint associated with this dialog */
+	/*! Endpoint associated with this dialog */
 	struct ast_sip_endpoint *endpoint;
 };
 
@@ -161,11 +246,14 @@ static pjsip_module endpoint_mod = {
 	.on_rx_request = endpoint_lookup,
 };
 
+#define SIP_MAX_QUEUE (AST_TASKPROCESSOR_HIGH_WATER_LEVEL * 3)
+
 static pj_bool_t distributor(pjsip_rx_data *rdata)
 {
 	pjsip_dialog *dlg = find_dialog(rdata);
 	struct distributor_dialog_data *dist = NULL;
 	struct ast_taskprocessor *serializer = NULL;
+	struct ast_taskprocessor *req_serializer = NULL;
 	pjsip_rx_data *clone;
 
 	if (dlg) {
@@ -175,11 +263,16 @@ static pj_bool_t distributor(pjsip_rx_data *rdata)
 		}
 	}
 
-	if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG && (
-		!pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_cancel_method) || 
-		!pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_bye_method)) &&
-		!serializer) {
-		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 481, NULL, NULL, NULL);
+	if (serializer) {
+		/* We have a serializer so we know where to send the message. */
+	} else if (rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG) {
+		req_serializer = find_request_serializer(rdata);
+		serializer = req_serializer;
+	} else if (!pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_cancel_method)
+		|| !pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_bye_method)) {
+		/* We have a BYE or CANCEL request without a serializer. */
+		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata,
+			PJSIP_SC_CALL_TSX_DOES_NOT_EXIST, NULL, NULL, NULL);
 		goto end;
 	}
 
@@ -189,12 +282,25 @@ static pj_bool_t distributor(pjsip_rx_data *rdata)
 		clone->endpt_info.mod_data[endpoint_mod.id] = ao2_bump(dist->endpoint);
 	}
 
-	ast_sip_push_task(serializer, distribute, clone);
+	if (ast_sip_threadpool_queue_size() > SIP_MAX_QUEUE) {
+		/* When the threadpool is backed up this much, there is a good chance that we have encountered
+		 * some sort of terrible condition and don't need to be adding more work to the threadpool.
+		 * It's in our best interest to send back a 503 response and be done with it.
+		 */
+		if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) {
+			pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 503, NULL, NULL, NULL);
+		}
+		ao2_cleanup(clone->endpt_info.mod_data[endpoint_mod.id]);
+		pjsip_rx_data_free_cloned(clone);
+	} else {
+		ast_sip_push_task(serializer, distribute, clone);
+	}
 
 end:
 	if (dlg) {
 		pjsip_dlg_dec_lock(dlg);
 	}
+	ast_taskprocessor_unreference(req_serializer);
 
 	return PJ_TRUE;
 }
@@ -222,7 +328,7 @@ struct ast_sip_auth *ast_sip_get_artificial_auth(void)
 	return artificial_auth;
 }
 
-static struct ast_sip_endpoint *artificial_endpoint;
+static struct ast_sip_endpoint *artificial_endpoint = NULL;
 
 static int create_artificial_endpoint(void)
 {
@@ -236,7 +342,7 @@ static int create_artificial_endpoint(void)
 	 * the proper size of the vector is returned. This value is
 	 * not actually used anywhere
 	 */
-	AST_VECTOR_APPEND(&artificial_endpoint->inbound_auths, "artificial-auth");
+	AST_VECTOR_APPEND(&artificial_endpoint->inbound_auths, ast_strdup("artificial-auth"));
 	return 0;
 }
 
@@ -373,13 +479,13 @@ int ast_sip_initialize_distributor(void)
 		return -1;
 	}
 
-	if (ast_sip_register_service(&distributor_mod)) {
+	if (internal_sip_register_service(&distributor_mod)) {
 		return -1;
 	}
-	if (ast_sip_register_service(&endpoint_mod)) {
+	if (internal_sip_register_service(&endpoint_mod)) {
 		return -1;
 	}
-	if (ast_sip_register_service(&auth_mod)) {
+	if (internal_sip_register_service(&auth_mod)) {
 		return -1;
 	}
 
@@ -388,9 +494,9 @@ int ast_sip_initialize_distributor(void)
 
 void ast_sip_destroy_distributor(void)
 {
-	ast_sip_unregister_service(&distributor_mod);
-	ast_sip_unregister_service(&endpoint_mod);
-	ast_sip_unregister_service(&auth_mod);
+	internal_sip_unregister_service(&distributor_mod);
+	internal_sip_unregister_service(&endpoint_mod);
+	internal_sip_unregister_service(&auth_mod);
 
 	ao2_cleanup(artificial_auth);
 	ao2_cleanup(artificial_endpoint);
diff --git a/res/res_pjsip/pjsip_global_headers.c b/res/res_pjsip/pjsip_global_headers.c
index eff8703..735008d 100644
--- a/res/res_pjsip/pjsip_global_headers.c
+++ b/res/res_pjsip/pjsip_global_headers.c
@@ -23,6 +23,7 @@
 
 #include "asterisk/res_pjsip.h"
 #include "asterisk/linkedlists.h"
+#include "include/res_pjsip_private.h"
 
 static pj_status_t add_request_headers(pjsip_tx_data *tdata);
 static pj_status_t add_response_headers(pjsip_tx_data *tdata);
@@ -111,6 +112,7 @@ static void remove_header(struct header_list *headers, const char *to_remove)
 	AST_LIST_TRAVERSE_SAFE_BEGIN(headers, iter, next) {
 		if (!strcasecmp(iter->name, to_remove)) {
 			AST_LIST_REMOVE_CURRENT(next);
+			destroy_header(iter);
 			break;
 		}
 	}
@@ -119,18 +121,22 @@ static void remove_header(struct header_list *headers, const char *to_remove)
 
 static int add_header(struct header_list *headers, const char *name, const char *value, int replace)
 {
-	struct header *to_add;
+	struct header *to_add = NULL;
 
-	to_add = alloc_header(name, value);
-	if (!to_add) {
-		return -1;
+	if (!ast_strlen_zero(value)) {
+		to_add = alloc_header(name, value);
+		if (!to_add) {
+			return -1;
+		}
 	}
 
 	AST_RWLIST_WRLOCK(headers);
 	if (replace) { 
 		remove_header(headers, name);
 	}
-	AST_LIST_INSERT_TAIL(headers, to_add, next);
+	if (to_add) {
+		AST_LIST_INSERT_TAIL(headers, to_add, next);
+	}
 	AST_RWLIST_UNLOCK(headers);
 
 	return 0;
@@ -151,7 +157,7 @@ void ast_sip_initialize_global_headers(void)
 	AST_RWLIST_HEAD_INIT(&request_headers);
 	AST_RWLIST_HEAD_INIT(&response_headers);
 
-	ast_sip_register_service(&global_header_mod);
+	internal_sip_register_service(&global_header_mod);
 }
 
 static void destroy_headers(struct header_list *headers)
@@ -168,4 +174,6 @@ void ast_sip_destroy_global_headers(void)
 {
 	destroy_headers(&request_headers);
 	destroy_headers(&response_headers);
+
+	internal_sip_unregister_service(&global_header_mod);
 }
diff --git a/res/res_pjsip/pjsip_options.c b/res/res_pjsip/pjsip_options.c
index 300f851..8b75a5f 100644
--- a/res/res_pjsip/pjsip_options.c
+++ b/res/res_pjsip/pjsip_options.c
@@ -28,36 +28,88 @@
 #include "asterisk/astobj2.h"
 #include "asterisk/cli.h"
 #include "asterisk/time.h"
+#include "asterisk/test.h"
+#include "asterisk/statsd.h"
 #include "include/res_pjsip_private.h"
 
 #define DEFAULT_LANGUAGE "en"
 #define DEFAULT_ENCODING "text/plain"
 #define QUALIFIED_BUCKETS 211
 
+static const char *status_map [] = {
+	[UNAVAILABLE] = "Unreachable",
+	[AVAILABLE] = "Reachable",
+	[UNKNOWN] = "Unknown",
+	[CREATED] = "Created",
+	[REMOVED] = "Removed",
+};
+
+static const char *short_status_map [] = {
+	[UNAVAILABLE] = "Unavail",
+	[AVAILABLE] = "Avail",
+	[UNKNOWN] = "Unknown",
+	[CREATED] = "Created",
+	[REMOVED] = "Removed",
+};
+
+const char *ast_sip_get_contact_status_label(const enum ast_sip_contact_status_type status)
+{
+	return status_map[status];
+}
+
+const char *ast_sip_get_contact_short_status_label(const enum ast_sip_contact_status_type status)
+{
+	return short_status_map[status];
+}
+
+/*!
+ * \internal
+ * \brief Destroy a ast_sip_contact_status object.
+ */
+static void contact_status_destroy(void * obj)
+{
+	struct ast_sip_contact_status *status = obj;
+
+	ast_free(status->aor);
+	ast_free(status->uri);
+}
+
 /*!
  * \internal
  * \brief Create a ast_sip_contact_status object.
  */
 static void *contact_status_alloc(const char *name)
 {
-	struct ast_sip_contact_status *status = ast_sorcery_generic_alloc(sizeof(*status), NULL);
+	struct ast_sip_contact_status *status = ast_sorcery_generic_alloc(sizeof(*status), contact_status_destroy);
+	char *id = ast_strdupa(name);
+	char *aor = id;
+	char *aor_separator = NULL;
 
 	if (!status) {
 		ast_log(LOG_ERROR, "Unable to allocate ast_sip_contact_status\n");
 		return NULL;
 	}
 
-	status->status = UNAVAILABLE;
+	/* Dynamic contacts are delimited with ";@" and static ones with "@@" */
+	if ((aor_separator = strstr(id, ";@")) || (aor_separator = strstr(id, "@@"))) {
+		*aor_separator = '\0';
+	}
+	ast_assert(aor_separator != NULL);
+
+	status->aor = ast_strdup(aor);
+	if (!status->aor) {
+		ao2_cleanup(status);
+		return NULL;
+	}
 
 	return status;
 }
 
 /*!
- * \internal
  * \brief Retrieve a ast_sip_contact_status object from sorcery creating
  *        one if not found.
  */
-static struct ast_sip_contact_status *find_or_create_contact_status(const struct ast_sip_contact *contact)
+struct ast_sip_contact_status *ast_res_pjsip_find_or_create_contact_status(const struct ast_sip_contact *contact)
 {
 	struct ast_sip_contact_status *status;
 
@@ -70,11 +122,20 @@ static struct ast_sip_contact_status *find_or_create_contact_status(const struct
 	status = ast_sorcery_alloc(ast_sip_get_sorcery(), CONTACT_STATUS,
 		ast_sorcery_object_get_id(contact));
 	if (!status) {
-		ast_log(LOG_ERROR, "Unable to create ast_sip_contact_status for contact %s\n",
-			contact->uri);
+		ast_log(LOG_ERROR, "Unable to create ast_sip_contact_status for contact %s/%s\n",
+			contact->aor, contact->uri);
 		return NULL;
 	}
 
+	status->uri = ast_strdup(contact->uri);
+	if (!status->uri) {
+		ao2_cleanup(status);
+		return NULL;
+	}
+
+	status->rtt_start = ast_tv(0, 0);
+	status->rtt = 0;
+
 	if (ast_sorcery_create(ast_sip_get_sorcery(), status)) {
 		ast_log(LOG_ERROR, "Unable to persist ast_sip_contact_status for contact %s\n",
 			contact->uri);
@@ -82,20 +143,10 @@ static struct ast_sip_contact_status *find_or_create_contact_status(const struct
 		return NULL;
 	}
 
-	return status;
-}
-
-static void delete_contact_status(const struct ast_sip_contact *contact)
-{
-	struct ast_sip_contact_status *status = ast_sorcery_retrieve_by_id(
-		ast_sip_get_sorcery(), CONTACT_STATUS, ast_sorcery_object_get_id(contact));
-
-	if (!status) {
-		return;
-	}
+	ast_statsd_log_string_va("PJSIP.contacts.states.%s", AST_STATSD_GAUGE,
+		"+1", 1.0, ast_sip_get_contact_status_label(status->status));
 
-	ast_sorcery_delete(ast_sip_get_sorcery(), status);
-	ao2_ref(status, -1);
+	return status;
 }
 
 /*!
@@ -105,39 +156,50 @@ static void delete_contact_status(const struct ast_sip_contact *contact)
 static void update_contact_status(const struct ast_sip_contact *contact,
 	enum ast_sip_contact_status_type value)
 {
-	struct ast_sip_contact_status *status;
-	struct ast_sip_contact_status *update;
+	RAII_VAR(struct ast_sip_contact_status *, status, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_sip_contact_status *, update, NULL, ao2_cleanup);
 
-	status = find_or_create_contact_status(contact);
+	status = ast_res_pjsip_find_or_create_contact_status(contact);
 	if (!status) {
+		ast_log(LOG_ERROR, "Unable to find ast_sip_contact_status for contact %s\n",
+			contact->uri);
 		return;
 	}
 
 	update = ast_sorcery_alloc(ast_sip_get_sorcery(), CONTACT_STATUS,
 		ast_sorcery_object_get_id(status));
 	if (!update) {
-		ast_log(LOG_ERROR, "Unable to create update ast_sip_contact_status for contact %s\n",
+		ast_log(LOG_ERROR, "Unable to allocate ast_sip_contact_status for contact %s\n",
 			contact->uri);
-		ao2_ref(status, -1);
 		return;
 	}
 
+	update->uri = ast_strdup(contact->uri);
+	if (!update->uri) {
+		return;
+	}
+
+	update->last_status = status->status;
 	update->status = value;
 
 	/* if the contact is available calculate the rtt as
 	   the diff between the last start time and "now" */
-	update->rtt = update->status == AVAILABLE ?
+	update->rtt = update->status == AVAILABLE && status->rtt_start.tv_sec > 0 ?
 		ast_tvdiff_us(ast_tvnow(), status->rtt_start) : 0;
-
 	update->rtt_start = ast_tv(0, 0);
 
+	ast_test_suite_event_notify("AOR_CONTACT_QUALIFY_RESULT",
+		"Contact: %s\r\n"
+		"Status: %s\r\n"
+		"RTT: %" PRId64,
+		ast_sorcery_object_get_id(update),
+		ast_sip_get_contact_status_label(update->status),
+		update->rtt);
+
 	if (ast_sorcery_update(ast_sip_get_sorcery(), update)) {
 		ast_log(LOG_ERROR, "Unable to update ast_sip_contact_status for contact %s\n",
 			contact->uri);
 	}
-
-	ao2_ref(update, -1);
-	ao2_ref(status, -1);
 }
 
 /*!
@@ -147,32 +209,38 @@ static void update_contact_status(const struct ast_sip_contact *contact,
  */
 static void init_start_time(const struct ast_sip_contact *contact)
 {
-	struct ast_sip_contact_status *status;
-	struct ast_sip_contact_status *update;
+	RAII_VAR(struct ast_sip_contact_status *, status, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_sip_contact_status *, update, NULL, ao2_cleanup);
 
-	status = find_or_create_contact_status(contact);
+	status = ast_res_pjsip_find_or_create_contact_status(contact);
 	if (!status) {
+		ast_log(LOG_ERROR, "Unable to find ast_sip_contact_status for contact %s\n",
+			contact->uri);
 		return;
 	}
 
 	update = ast_sorcery_alloc(ast_sip_get_sorcery(), CONTACT_STATUS,
 		ast_sorcery_object_get_id(status));
 	if (!update) {
-		ast_log(LOG_ERROR, "Unable to create update ast_sip_contact_status for contact %s\n",
+		ast_log(LOG_ERROR, "Unable to copy ast_sip_contact_status for contact %s\n",
 			contact->uri);
-		ao2_ref(status, -1);
 		return;
 	}
 
+	update->uri = ast_strdup(contact->uri);
+	if (!update->uri) {
+		return;
+	}
+
+	update->status = status->status;
+	update->last_status = status->last_status;
+	update->rtt = status->rtt;
 	update->rtt_start = ast_tvnow();
 
 	if (ast_sorcery_update(ast_sip_get_sorcery(), update)) {
 		ast_log(LOG_ERROR, "Unable to update ast_sip_contact_status for contact %s\n",
 			contact->uri);
 	}
-
-	ao2_ref(update, -1);
-	ao2_ref(status, -1);
 }
 
 /*!
@@ -261,7 +329,7 @@ static void qualify_contact_cb(void *token, pjsip_event *e)
 
 	switch(e->body.tsx_state.type) {
 	default:
-		ast_log(LOG_ERROR, "Unexpected PJSIP event %d\n", e->body.tsx_state.type);
+		ast_log(LOG_ERROR, "Unexpected PJSIP event %u\n", e->body.tsx_state.type);
 		/* Fall through */
 	case PJSIP_EVENT_TRANSPORT_ERROR:
 	case PJSIP_EVENT_TIMER:
@@ -320,7 +388,7 @@ static int qualify_contact(struct ast_sip_endpoint *endpoint, struct ast_sip_con
 	init_start_time(contact);
 
 	ao2_ref(contact, +1);
-	if (ast_sip_send_request(tdata, NULL, endpoint_local, contact, qualify_contact_cb)
+	if (ast_sip_send_out_of_dialog_request(tdata, endpoint_local, (int)(contact->qualify_timeout * 1000), contact, qualify_contact_cb)
 		!= PJ_SUCCESS) {
 		ast_log(LOG_ERROR, "Unable to send request to qualify contact %s\n",
 			contact->uri);
@@ -418,14 +486,14 @@ static int qualify_contact_sched(const void *obj)
 	 * up the data object ref between self deletion and an external
 	 * deletion.
 	 */
-	return 1;
+	return data->contact->qualify_frequency * 1000;
 }
 
 /*!
  * \internal
  * \brief Set up a scheduled qualify contact check.
  */
-static void schedule_qualify(struct ast_sip_contact *contact)
+static void schedule_qualify(struct ast_sip_contact *contact, int initial_interval)
 {
 	struct sched_data *data;
 
@@ -437,8 +505,8 @@ static void schedule_qualify(struct ast_sip_contact *contact)
 	ast_assert(contact->qualify_frequency != 0);
 
 	ao2_t_ref(data, +1, "Ref for qualify_contact_sched() scheduler entry");
-	data->id = ast_sched_add_variable(sched, contact->qualify_frequency * 1000,
-		qualify_contact_sched, data, 0);
+	data->id = ast_sched_add_variable(sched, initial_interval,
+		qualify_contact_sched, data, 1);
 	if (data->id < 0) {
 		ao2_t_ref(data, -1, "Cleanup failed scheduler add");
 		ast_log(LOG_ERROR, "Unable to schedule qualify for contact %s\n",
@@ -482,9 +550,9 @@ static void qualify_and_schedule(struct ast_sip_contact *contact)
 			ao2_ref(contact, -1);
 		}
 
-		schedule_qualify(contact);
+		schedule_qualify(contact, contact->qualify_frequency * 1000);
 	} else {
-		delete_contact_status(contact);
+		update_contact_status(contact, UNKNOWN);
 	}
 }
 
@@ -577,7 +645,6 @@ static pj_status_t send_options_response(pjsip_rx_data *rdata, int code)
 	pjsip_transaction *trans = pjsip_rdata_get_tsx(rdata);
 	pjsip_tx_data *tdata;
 	const pjsip_hdr *hdr;
-	pjsip_response_addr res_addr;
 	pj_status_t status;
 
 	/* Make the response object */
@@ -609,17 +676,11 @@ static pj_status_t send_options_response(pjsip_rx_data *rdata, int code)
 	if (dlg && trans) {
 		status = pjsip_dlg_send_response(dlg, trans, tdata);
 	} else {
-		/* Get where to send request. */
-		if ((status = pjsip_get_response_addr(
-			     tdata->pool, rdata, &res_addr)) != PJ_SUCCESS) {
-			ast_log(LOG_ERROR, "Unable to get response address (%d)\n",
-				status);
-
-			pjsip_tx_data_dec_ref(tdata);
-			return status;
-		}
-		status = ast_sip_send_response(&res_addr, tdata,
-						   ast_pjsip_rdata_get_endpoint(rdata));
+		struct ast_sip_endpoint *endpoint;
+
+		endpoint = ast_pjsip_rdata_get_endpoint(rdata);
+		status = ast_sip_send_stateful_response(rdata, tdata, endpoint);
+		ao2_cleanup(endpoint);
 	}
 
 	if (status != PJ_SUCCESS) {
@@ -648,13 +709,17 @@ static pj_bool_t options_on_rx_request(pjsip_rx_data *rdata)
 	ruri = rdata->msg_info.msg->line.req.uri;
 	if (!PJSIP_URI_SCHEME_IS_SIP(ruri) && !PJSIP_URI_SCHEME_IS_SIPS(ruri)) {
 		send_options_response(rdata, 416);
-		return -1;
+		return PJ_TRUE;
 	}
 
 	sip_ruri = pjsip_uri_get_uri(ruri);
 	ast_copy_pj_str(exten, &sip_ruri->user, sizeof(exten));
 
 	if (ast_shutting_down()) {
+		/*
+		 * Not taking any new calls at this time.
+		 * Likely a server availability OPTIONS poll.
+		 */
 		send_options_response(rdata, 503);
 	} else if (!ast_strlen_zero(exten) && !ast_exists_extension(NULL, endpoint->context, exten, 1, NULL)) {
 		send_options_response(rdata, 404);
@@ -926,6 +991,35 @@ static int sched_qualifies_cmp_fn(void *obj, void *arg, int flags)
 	return CMP_MATCH;
 }
 
+static int rtt_start_handler(const struct aco_option *opt,
+	struct ast_variable *var, void *obj)
+{
+	struct ast_sip_contact_status *status = obj;
+	long int sec, usec;
+
+	if (sscanf(var->value, "%ld.%06ld", &sec, &usec) != 2) {
+		return -1;
+	}
+
+	status->rtt_start = ast_tv(sec, usec);
+
+	return 0;
+}
+
+static int rtt_start_to_str(const void *obj, const intptr_t *args, char **buf)
+{
+	const struct ast_sip_contact_status *status = obj;
+
+	if (ast_asprintf(buf, "%ld.%06ld", (long)status->rtt_start.tv_sec, (long)status->rtt_start.tv_usec) == -1) {
+		return -1;
+	}
+
+	return 0;
+}
+
+static char status_value_unknown[2];
+static char status_value_created[2];
+
 int ast_sip_initialize_sorcery_qualify(void)
 {
 	struct ast_sorcery *sorcery = ast_sip_get_sorcery();
@@ -939,10 +1033,16 @@ int ast_sip_initialize_sorcery_qualify(void)
 		return -1;
 	}
 
-	ast_sorcery_object_field_register_nodoc(sorcery, CONTACT_STATUS, "rtt", "0", OPT_UINT_T,
-					  1, FLDSET(struct ast_sip_contact_status, status));
-	ast_sorcery_object_field_register_nodoc(sorcery, CONTACT_STATUS, "rtt", "0", OPT_UINT_T,
-					  1, FLDSET(struct ast_sip_contact_status, rtt));
+	snprintf(status_value_unknown, sizeof(status_value_unknown), "%u", UNKNOWN);
+	ast_sorcery_object_field_register_nodoc(sorcery, CONTACT_STATUS, "last_status",
+		status_value_unknown, OPT_UINT_T, 1, FLDSET(struct ast_sip_contact_status, last_status));
+	snprintf(status_value_created, sizeof(status_value_created), "%u", CREATED);
+	ast_sorcery_object_field_register_nodoc(sorcery, CONTACT_STATUS, "status",
+		status_value_created, OPT_UINT_T, 1, FLDSET(struct ast_sip_contact_status, status));
+	ast_sorcery_object_field_register_custom_nodoc(sorcery, CONTACT_STATUS, "rtt_start",
+		"0.0", rtt_start_handler, rtt_start_to_str, NULL, 0, 0);
+	ast_sorcery_object_field_register_nodoc(sorcery, CONTACT_STATUS, "rtt",
+		"0", OPT_UINT_T, 1, FLDSET(struct ast_sip_contact_status, rtt));
 
 	return 0;
 }
@@ -951,11 +1051,28 @@ static int qualify_and_schedule_cb(void *obj, void *arg, int flags)
 {
 	struct ast_sip_contact *contact = obj;
 	struct ast_sip_aor *aor = arg;
+	int initial_interval;
+	int max_time = ast_sip_get_max_initial_qualify_time();
 
 	contact->qualify_frequency = aor->qualify_frequency;
+	contact->qualify_timeout = aor->qualify_timeout;
 	contact->authenticate_qualify = aor->authenticate_qualify;
 
-	qualify_and_schedule(contact);
+	/* Delay initial qualification by a random fraction of the specified interval */
+	if (max_time && max_time < contact->qualify_frequency) {
+		initial_interval = max_time;
+	} else {
+		initial_interval = contact->qualify_frequency;
+	}
+
+	initial_interval = (int)((initial_interval * 1000) * ast_random_double());
+
+	unschedule_qualify(contact);
+	if (contact->qualify_frequency) {
+		schedule_qualify(contact, initial_interval);
+	} else {
+		update_contact_status(contact, UNKNOWN);
+	}
 
 	return 0;
 }
@@ -999,10 +1116,25 @@ static int qualify_and_schedule_all_cb(void *obj, void *arg, int flags)
 	return 0;
 }
 
+/*!
+ * \internal
+ * \brief Unschedule all existing contacts
+ */
+static int unschedule_all_cb(void *obj, void *arg, int flags)
+{
+	struct sched_data *data = obj;
+
+	AST_SCHED_DEL_UNREF(sched, data->id, ao2_ref(data, -1));
+
+	return CMP_MATCH;
+}
+
 static void qualify_and_schedule_all(void)
 {
 	struct ao2_container *endpoints = ast_sip_get_endpoints();
 
+	ao2_callback(sched_qualifies, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, unschedule_all_cb, NULL);
+
 	if (!endpoints) {
 		return;
 	}
@@ -1011,11 +1143,6 @@ static void qualify_and_schedule_all(void)
 	ao2_ref(endpoints, -1);
 }
 
-static const char *status_map [] = {
-	[UNAVAILABLE] = "Unreachable",
-	[AVAILABLE] = "Reachable",
-};
-
 static int format_contact_status(void *obj, void *arg, int flags)
 {
 	struct ast_sip_contact_wrapper *wrapper = obj;
@@ -1036,12 +1163,11 @@ static int format_contact_status(void *obj, void *arg, int flags)
 
 	ast_str_append(&buf, 0, "AOR: %s\r\n", wrapper->aor_id);
 	ast_str_append(&buf, 0, "URI: %s\r\n", contact->uri);
-	if (status) {
-		ast_str_append(&buf, 0, "Status: %s\r\n", status_map[status->status]);
-		ast_str_append(&buf, 0, "RoundtripUsec: %" PRId64 "\r\n", status->rtt);
-	} else {
-		ast_str_append(&buf, 0, "Status: Unknown\r\n");
+	ast_str_append(&buf, 0, "Status: %s\r\n", ast_sip_get_contact_status_label(status->status));
+	if (status->status == UNKNOWN) {
 		ast_str_append(&buf, 0, "RoundtripUsec: N/A\r\n");
+	} else {
+		ast_str_append(&buf, 0, "RoundtripUsec: %" PRId64 "\r\n", status->rtt);
 	}
 	ast_str_append(&buf, 0, "EndpointName: %s\r\n",
 			ast_sorcery_object_get_id(endpoint));
@@ -1071,6 +1197,43 @@ static struct ast_sip_endpoint_formatter contact_status_formatter = {
 	.format_ami = format_ami_contact_status
 };
 
+static void aor_observer_modified(const void *obj)
+{
+	struct ast_sip_aor *aor = (void *)obj;
+	struct ao2_container *contacts;
+
+	contacts = ast_sip_location_retrieve_aor_contacts(aor);
+	if (contacts) {
+		ao2_callback(contacts, OBJ_NODATA, qualify_and_schedule_cb, aor);
+		ao2_ref(contacts, -1);
+	}
+}
+
+static int unschedule_contact_cb(void *obj, void *arg, int flags)
+{
+	unschedule_qualify(obj);
+
+	return CMP_MATCH;
+}
+
+static void aor_observer_deleted(const void *obj)
+{
+	const struct ast_sip_aor *aor = obj;
+	struct ao2_container *contacts;
+
+	contacts = ast_sip_location_retrieve_aor_contacts(aor);
+	if (contacts) {
+		ao2_callback(contacts, OBJ_NODATA, unschedule_contact_cb, NULL);
+		ao2_ref(contacts, -1);
+	}
+}
+
+static const struct ast_sorcery_observer observer_callbacks_options = {
+	.created = aor_observer_modified,
+	.updated = aor_observer_modified,
+	.deleted = aor_observer_deleted
+};
+
 int ast_res_pjsip_init_options_handling(int reload)
 {
 	static const pj_str_t STR_OPTIONS = { "OPTIONS", 7 };
@@ -1101,7 +1264,14 @@ int ast_res_pjsip_init_options_handling(int reload)
 		return -1;
 	}
 
-	ast_sip_register_endpoint_formatter(&contact_status_formatter);
+	if (ast_sorcery_observer_add(ast_sip_get_sorcery(), "aor", &observer_callbacks_options)) {
+		pjsip_endpt_unregister_module(ast_sip_get_pjsip_endpoint(), &options_module);
+		ao2_cleanup(sched_qualifies);
+		sched_qualifies = NULL;
+		return -1;
+	}
+
+	internal_sip_register_endpoint_formatter(&contact_status_formatter);
 	ast_manager_register2("PJSIPQualify", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_sip_qualify, NULL, NULL, NULL);
 	ast_cli_register_multiple(cli_options, ARRAY_LEN(cli_options));
 
@@ -1114,8 +1284,9 @@ void ast_res_pjsip_cleanup_options_handling(void)
 {
 	ast_cli_unregister_multiple(cli_options, ARRAY_LEN(cli_options));
 	ast_manager_unregister("PJSIPQualify");
-	ast_sip_unregister_endpoint_formatter(&contact_status_formatter);
+	internal_sip_unregister_endpoint_formatter(&contact_status_formatter);
 
+	ast_sorcery_observer_remove(ast_sip_get_sorcery(), "aor", &observer_callbacks_options);
 	pjsip_endpt_unregister_module(ast_sip_get_pjsip_endpoint(), &options_module);
 	ao2_cleanup(sched_qualifies);
 	sched_qualifies = NULL;
diff --git a/res/res_pjsip/pjsip_outbound_auth.c b/res/res_pjsip/pjsip_outbound_auth.c
index 28ca3ec..1f75422 100644
--- a/res/res_pjsip/pjsip_outbound_auth.c
+++ b/res/res_pjsip/pjsip_outbound_auth.c
@@ -91,6 +91,11 @@ int ast_sip_dialog_setup_outbound_authentication(pjsip_dialog *dlg, const struct
 	return 0;
 }
 
-int ast_sip_initialize_outbound_authentication(void) {
-	return ast_sip_register_service(&outbound_auth_mod);
+int internal_sip_initialize_outbound_authentication(void) {
+	return internal_sip_register_service(&outbound_auth_mod);
+}
+
+
+void internal_sip_destroy_outbound_authentication(void) {
+	internal_sip_unregister_service(&outbound_auth_mod);
 }
diff --git a/res/res_pjsip/presence_xml.c b/res/res_pjsip/presence_xml.c
index 2fe6bdc..b98ea02 100644
--- a/res/res_pjsip/presence_xml.c
+++ b/res/res_pjsip/presence_xml.c
@@ -16,14 +16,6 @@
  * at the top of the source tree.
  */
 
-/*** MODULEINFO
-	<depend>pjproject</depend>
-	<depend>res_pjsip</depend>
-	<depend>res_pjsip_pubsub</depend>
-	<depend>res_pjsip_exten_state</depend>
-	<support_level>core</support_level>
- ***/
-
 #include "asterisk.h"
 
 #include <pjsip.h>
@@ -31,54 +23,61 @@
 #include <pjlib.h>
 
 #include "asterisk/module.h"
-#include "asterisk/res_pjsip.h"
-#include "asterisk/res_pjsip_pubsub.h"
+#include "asterisk/pbx.h"
 #include "asterisk/res_pjsip_presence_xml.h"
-#include "asterisk/res_pjsip_body_generator_types.h"
 
 void ast_sip_sanitize_xml(const char *input, char *output, size_t len)
 {
 	char *copy = ast_strdupa(input);
 	char *break_point;
+	size_t remaining = len - 1;
 
 	output[0] = '\0';
 
-	while ((break_point = strpbrk(copy, "<>\"&'\n\r"))) {
+	while ((break_point = strpbrk(copy, "<>\"&'\n\r")) && remaining) {
 		char to_escape = *break_point;
 
 		*break_point = '\0';
-		strncat(output, copy, len);
+		strncat(output, copy, remaining);
+
+		/* The strncat function will write remaining+1 if the string length is
+		 * equal to or greater than the size provided to it. We take this into
+		 * account by subtracting 1, which ensures that the NULL byte is written
+		 * inside of the provided buffer.
+		 */
+		remaining = len - strlen(output) - 1;
 
 		switch (to_escape) {
 		case '<':
-			strncat(output, "<", len);
+			strncat(output, "<", remaining);
 			break;
 		case '>':
-			strncat(output, ">", len);
+			strncat(output, ">", remaining);
 			break;
 		case '"':
-			strncat(output, """, len);
+			strncat(output, """, remaining);
 			break;
 		case '&':
-			strncat(output, "&", len);
+			strncat(output, "&", remaining);
 			break;
 		case '\'':
-			strncat(output, "'", len);
+			strncat(output, "'", remaining);
 			break;
 		case '\r':
-			strncat(output, "
", len);
+			strncat(output, "
", remaining);
 			break;
 		case '\n':
-			strncat(output, "
", len);
+			strncat(output, "
", remaining);
 			break;
 		};
 
 		copy = break_point + 1;
+		remaining = len - strlen(output) - 1;
 	}
 
 	/* Be sure to copy everything after the final bracket */
-	if (*copy) {
-		strncat(output, copy, len);
+	if (*copy && remaining) {
+		strncat(output, copy, remaining);
 	}
 }
 
diff --git a/res/res_pjsip/security_events.c b/res/res_pjsip/security_events.c
index d19e5ba..5c30e1c 100644
--- a/res/res_pjsip/security_events.c
+++ b/res/res_pjsip/security_events.c
@@ -26,7 +26,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 412400 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <pjsip.h>
 
diff --git a/res/res_pjsip_acl.c b/res/res_pjsip_acl.c
index 115d1b8..a04f2b4 100644
--- a/res/res_pjsip_acl.c
+++ b/res/res_pjsip_acl.c
@@ -139,9 +139,11 @@ static int extract_contact_addr(pjsip_contact_hdr *contact, struct ast_sockaddr
 	char host[256];
 
 	if (!contact || contact->star) {
+		*addrs = NULL;
 		return 0;
 	}
 	if (!PJSIP_URI_SCHEME_IS_SIP(contact->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact->uri)) {
+		*addrs = NULL;
 		return 0;
 	}
 	sip_uri = pjsip_uri_get_uri(contact->uri);
@@ -269,6 +271,7 @@ static int load_module(void)
 {
 	CHECK_PJSIP_MODULE_LOADED();
 
+	ast_sorcery_apply_config(ast_sip_get_sorcery(), SIP_SORCERY_ACL_TYPE);
 	ast_sorcery_apply_default(ast_sip_get_sorcery(), SIP_SORCERY_ACL_TYPE,
 				  "config", "pjsip.conf,criteria=type=acl");
 
diff --git a/res/res_pjsip_caller_id.c b/res/res_pjsip_caller_id.c
index e22ce6a..f1908a7 100644
--- a/res/res_pjsip_caller_id.c
+++ b/res/res_pjsip_caller_id.c
@@ -131,12 +131,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) {
-		return 0;
-	}
-	if (!pj_stricmp2(&privacy->hvalue, "id")) {
+	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 {
+		id->number.presentation = AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED;
+		id->name.presentation = AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED;
 	}
 
 	return 0;
@@ -176,12 +176,18 @@ static int set_id_from_rpid(pjsip_rx_data *rdata, struct ast_party_id *id)
 	privacy = pjsip_param_find(&rpid_hdr->other_param, &privacy_str);
 	screen = pjsip_param_find(&rpid_hdr->other_param, &screen_str);
 	if (privacy && !pj_stricmp2(&privacy->value, "full")) {
-		id->number.presentation |= AST_PRES_RESTRICTED;
-		id->name.presentation |= AST_PRES_RESTRICTED;
+		id->number.presentation = AST_PRES_RESTRICTED;
+		id->name.presentation = AST_PRES_RESTRICTED;
+	} else {
+		id->number.presentation = AST_PRES_ALLOWED;
+		id->name.presentation = AST_PRES_ALLOWED;
 	}
 	if (screen && !pj_stricmp2(&screen->value, "yes")) {
 		id->number.presentation |= AST_PRES_USER_NUMBER_PASSED_SCREEN;
 		id->name.presentation |= AST_PRES_USER_NUMBER_PASSED_SCREEN;
+	} else {
+		id->number.presentation |= AST_PRES_USER_NUMBER_UNSCREENED;
+		id->name.presentation |= AST_PRES_USER_NUMBER_UNSCREENED;
 	}
 
 	return 0;
@@ -345,10 +351,11 @@ static void update_incoming_connected_line(struct ast_sip_session *session, pjsi
  */
 static int caller_id_incoming_request(struct ast_sip_session *session, pjsip_rx_data *rdata)
 {
-	if (session->inv_session->state < PJSIP_INV_STATE_CONFIRMED) {
+	if (!session->channel) {
 		/*
-		 * Initial inbound INVITE.  Set the session ID directly
-		 * because the channel has not been created yet.
+		 * Since we have no channel this must be the initial inbound
+		 * INVITE.  Set the session ID directly because the channel
+		 * has not been created yet.
 		 */
 		if (session->endpoint->id.trust_inbound
 			&& (!set_id_from_pai(rdata, &session->id)
@@ -362,8 +369,9 @@ static int caller_id_incoming_request(struct ast_sip_session *session, pjsip_rx_
 			set_id_from_from(rdata, &session->id);
 		}
 	} else {
-		/* Reinvite. Check for changes to the ID and queue a connected line
-		 * update if necessary
+		/*
+		 * ReINVITE or UPDATE.  Check for changes to the ID and queue
+		 * a connected line update if necessary.
 		 */
 		update_incoming_connected_line(session, rdata);
 	}
@@ -475,8 +483,7 @@ static void add_privacy_header(pjsip_tx_data *tdata, const struct ast_party_id *
 
 	old_privacy = pjsip_msg_find_hdr_by_name(tdata->msg, &pj_privacy_name, NULL);
 
-	if ((id->name.presentation & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED &&
-			(id->number.presentation & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED) {
+	if ((ast_party_id_presentation(id) & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED) {
 		if (old_privacy) {
 			pj_list_erase(old_privacy);
 		}
@@ -499,10 +506,6 @@ static void add_pai_header(pjsip_tx_data *tdata, const struct ast_party_id *id)
 	pjsip_fromto_hdr *pai_hdr;
 	pjsip_fromto_hdr *old_pai;
 
-	if (!id->number.valid) {
-		return;
-	}
-
 	/* Since inv_session reuses responses, we have to make sure there's not already
 	 * a P-Asserted-Identity present. If there is, we just modify the old one.
 	 */
@@ -546,6 +549,7 @@ static void add_privacy_params(pjsip_tx_data *tdata, pjsip_fromto_hdr *hdr, cons
 	pjsip_param *old_screen;
 	pjsip_param *privacy;
 	pjsip_param *screen;
+	int presentation;
 
 	old_privacy = pjsip_param_find(&hdr->other_param, &privacy_str);
 	old_screen = pjsip_param_find(&hdr->other_param, &screen_str);
@@ -566,15 +570,13 @@ static void add_privacy_params(pjsip_tx_data *tdata, pjsip_fromto_hdr *hdr, cons
 		screen = old_screen;
 	}
 
-	if ((id->name.presentation & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED &&
-			(id->name.presentation & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED) {
+	presentation = ast_party_id_presentation(id);
+	if ((presentation & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED) {
 		privacy->value = privacy_off_str;
 	} else {
 		privacy->value = privacy_full_str;
 	}
-
-	if ((id->name.presentation & AST_PRES_NUMBER_TYPE) == AST_PRES_USER_NUMBER_PASSED_SCREEN &&
-			(id->number.presentation & AST_PRES_NUMBER_TYPE) == AST_PRES_USER_NUMBER_PASSED_SCREEN) {
+	if ((presentation & AST_PRES_NUMBER_TYPE) == AST_PRES_USER_NUMBER_PASSED_SCREEN) {
 		screen->value = screen_yes_str;
 	} else {
 		screen->value = screen_no_str;
@@ -593,10 +595,6 @@ static void add_rpid_header(pjsip_tx_data *tdata, const struct ast_party_id *id)
 	pjsip_fromto_hdr *rpid_hdr;
 	pjsip_fromto_hdr *old_rpid;
 
-	if (!id->number.valid) {
-		return;
-	}
-
 	/* Since inv_session reuses responses, we have to make sure there's not already
 	 * a P-Asserted-Identity present. If there is, we just modify the old one.
 	 */
@@ -628,9 +626,9 @@ static void add_rpid_header(pjsip_tx_data *tdata, const struct ast_party_id *id)
  */
 static void add_id_headers(const struct ast_sip_session *session, pjsip_tx_data *tdata, const struct ast_party_id *id)
 {
-	if (((id->name.presentation & AST_PRES_RESTRICTION) == AST_PRES_RESTRICTED ||
-			(id->number.presentation & AST_PRES_RESTRICTION) == AST_PRES_RESTRICTED) &&
-			!session->endpoint->id.trust_outbound) {
+	if (!id->number.valid
+		|| (!session->endpoint->id.trust_outbound
+			&& (ast_party_id_presentation(id) & AST_PRES_RESTRICTION) != AST_PRES_ALLOWED)) {
 		return;
 	}
 	if (session->endpoint->id.send_pai) {
@@ -669,11 +667,7 @@ static void caller_id_outgoing_request(struct ast_sip_session *session, pjsip_tx
 	ast_party_id_copy(&connected_id, &effective_id);
 	ast_channel_unlock(session->channel);
 
-	if (session->inv_session->state < PJSIP_INV_STATE_CONFIRMED &&
-			ast_strlen_zero(session->endpoint->fromuser) &&
-			(session->endpoint->id.trust_outbound ||
-			((connected_id.name.presentation & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED &&
-			(connected_id.number.presentation & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED))) {
+	if (session->inv_session->state < PJSIP_INV_STATE_CONFIRMED) {
 		/* Only change the From header on the initial outbound INVITE. Switching it
 		 * mid-call might confuse some UAs.
 		 */
@@ -683,8 +677,15 @@ static void caller_id_outgoing_request(struct ast_sip_session *session, pjsip_tx
 		from = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_FROM, tdata->msg->hdr.next);
 		dlg = session->inv_session->dlg;
 
-		modify_id_header(tdata->pool, from, &connected_id);
-		modify_id_header(dlg->pool, dlg->local.info, &connected_id);
+		if (ast_strlen_zero(session->endpoint->fromuser)
+			&& (session->endpoint->id.trust_outbound
+				|| (ast_party_id_presentation(&connected_id) & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED)) {
+			modify_id_header(tdata->pool, from, &connected_id);
+			modify_id_header(dlg->pool, dlg->local.info, &connected_id);
+		}
+
+		ast_sip_add_usereqphone(session->endpoint, tdata->pool, from->uri);
+		ast_sip_add_usereqphone(session->endpoint, dlg->pool, dlg->local.info->uri);
 	}
 	add_id_headers(session, tdata, &connected_id);
 	ast_party_id_free(&connected_id);
diff --git a/res/res_pjsip_config_wizard.c b/res/res_pjsip_config_wizard.c
new file mode 100644
index 0000000..39d3c3f
--- /dev/null
+++ b/res/res_pjsip_config_wizard.c
@@ -0,0 +1,1202 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2014, Fairview 5 Engineering, LLC
+ *
+ * George Joseph <george.joseph at fairview5.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 PJSIP Configuration Wizard
+ *
+ * \author George Joseph <george.joseph at fairview5.com>
+  */
+
+/*! \li \ref res_pjsip_config_wizard.c uses the configuration file \ref pjsip_wizard.conf
+ */
+
+/*!
+ * \page pjsip_wizard.conf pjsip_wizard.conf
+ * \verbinclude pjsip_wizard.conf.sample
+ */
+
+/*** MODULEINFO
+	<depend>pjproject</depend>
+	<depend>res_pjsip</depend>
+	<support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <regex.h>
+#include <pjsip.h>
+
+#include "asterisk/astobj2.h"
+#include "asterisk/res_pjsip.h"
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+#include "asterisk/sorcery.h"
+#include "asterisk/vector.h"
+
+/*** DOCUMENTATION
+	<configInfo name="res_pjsip_config_wizard" language="en_US">
+		<synopsis>Module that privides simple configuration wizard capabilities.</synopsis>
+		<description><para>
+			<emphasis>PJSIP Configuration Wizard</emphasis>
+			</para>
+			<para>This module allows creation of common PJSIP configuration scenarios
+			without having to specify individual endpoint, aor, auth, identify and registration objects.
+			</para>
+			<para> </para>
+
+			<para>For example, the following configuration snippet would create the
+			endpoint, aor, contact, auth and phoneprov objects necessary for a phone to
+			get phone provisioning information, register, and make and receive calls.
+			A hint is also created in the default context for extension 1000.</para>
+			<para> </para>
+
+			<para>[myphone]</para>
+			<para>type = wizard</para>
+			<para>sends_auth = no</para>
+			<para>accepts_auth = yes</para>
+			<para>sends_registrations = no</para>
+			<para>accepts_registrations = yes</para>
+			<para>has_phoneprov = yes</para>
+			<para>transport = ipv4</para>
+			<para>has_hint = yes</para>
+			<para>hint_exten = 1000</para>
+			<para>inbound_auth/username = testname</para>
+			<para>inbound_auth/password = test password</para>
+			<para>endpoint/allow = ulaw</para>
+			<para>endpoint/context = default</para>
+			<para>phoneprov/MAC = 001122aa4455</para>
+			<para>phoneprov/PROFILE = profile1</para>
+			<para> </para>
+
+			<para>The first 8 items are specific to the wizard.  The rest of the items
+			are passed verbatim to the underlying objects.</para>
+			<para> </para>
+
+			<para>The following configuration snippet would create the
+			endpoint, aor, contact, auth, identify and registration objects necessary for a trunk
+			to another pbx or ITSP that requires registration.</para>
+			<para> </para>
+
+			<para>[mytrunk]</para>
+			<para>type = wizard</para>
+			<para>sends_auth = yes</para>
+			<para>accepts_auth = no</para>
+			<para>sends_registrations = yes</para>
+			<para>accepts_registrations = no</para>
+			<para>transport = ipv4</para>
+			<para>remote_hosts = sip1.myitsp.com:5060,sip2.myitsp.com:5060</para>
+			<para>outbound_auth/username = testname</para>
+			<para>outbound_auth/password = test password</para>
+			<para>endpoint/allow = ulaw</para>
+			<para>endpoint/context = default</para>
+			<para> </para>
+
+			<para>Of course, any of the items in either example could be placed into
+			templates and shared among wizard objects.</para>
+
+			<para> </para>
+			<para>For more information, visit:</para>
+			<para><literal>https://wiki.asterisk.org/wiki/display/AST/PJSIP+Configuration+Wizard</literal></para>
+		</description>
+
+		<configFile name="pjsip_wizard.conf">
+			<configObject name="wizard">
+				<synopsis>Provides config wizard.</synopsis>
+				<description>
+				<para>For more information, visit:</para>
+				<para><literal>https://wiki.asterisk.org/wiki/display/AST/PJSIP+Configuration+Wizard</literal></para>
+				</description>
+				<configOption name="type">
+					<synopsis>Must be 'wizard'.</synopsis>
+				</configOption>
+				<configOption name="transport">
+					<synopsis>The name of a transport to use for this object.</synopsis>
+					<description><para>If not specified,
+					the default will be used.</para></description>
+				</configOption>
+				<configOption name="remote_hosts">
+					<synopsis>List of remote hosts.</synopsis>
+					<description><para>A comma-separated list of remote hosts in the form of
+					<replaceable>host</replaceable>[:<replaceable>port</replaceable>].
+					If set, an aor static contact and an identify match will be created for each
+					entry in the list.  If send_registrations is also set, a registration will
+					also be created for each.</para></description>
+				</configOption>
+				<configOption name="sends_auth" default="no">
+					<synopsis>Send outbound authentication to remote hosts.</synopsis>
+					<description><para>At least outbound_auth/username is required.</para></description>
+				</configOption>
+				<configOption name="accepts_auth" default="no">
+					<synopsis>Accept incoming authentication from remote hosts.</synopsis>
+					<description><para>At least inbound_auth/username is required.</para></description>
+				</configOption>
+				<configOption name="sends_registrations" default="no">
+					<synopsis>Send outbound registrations to remote hosts.</synopsis>
+					<description><para>remote_hosts is required and a registration object will
+					be created for each host in the remote _hosts string.  If authentication is required,
+					sends_auth and an outbound_auth/username must also be supplied.</para></description>
+				</configOption>
+				<configOption name="accepts_registrations" default="no">
+					<synopsis>Accept inbound registration from remote hosts.</synopsis>
+					<description><para>An AOR with dynamic contacts will be created.  If
+					the number of contacts nneds to be limited, set aor/max_contacts.</para></description>
+				</configOption>
+				<configOption name="has_phoneprov" default="no">
+					<synopsis>Create a phoneprov object for this endpoint.</synopsis>
+					<description><para>A phoneprov object will be created.  phoneprov/MAC
+					must be specified.</para></description>
+				</configOption>
+				<configOption name="server_uri_pattern" default="sip:${REMOTE_HOST}">
+					<synopsis>A pattern to use for constructing outbound registration server_uris.</synopsis>
+					<description><para>
+					The literal <literal>${REMOTE_HOST}</literal> will be substituted with the
+					appropriate remote_host for each registration.</para></description>
+				</configOption>
+				<configOption name="client_uri_pattern" default="sip:${USERNAME}@${REMOTE_HOST}">
+					<synopsis>A pattern to use for constructing outbound registration client_uris.</synopsis>
+					<description><para>
+					The literals <literal>${REMOTE_HOST}</literal> and <literal>${USERNAME}</literal>
+					will be substituted with the appropriate remote_host and outbound_auth/username.</para></description>
+				</configOption>
+				<configOption name="contact_pattern" default="sip:${REMOTE_HOST}">
+					<synopsis>A pattern to use for constructing outbound contact uris.</synopsis>
+					<description><para>
+					The literal <literal>${REMOTE_HOST}</literal> will be substituted with the
+					appropriate remote_host for each contact.</para></description>
+				</configOption>
+				<configOption name="has_hint" default="no">
+					<synopsis>Create hint and optionally a default application.</synopsis>
+					<description><para>Create hint and optionally a default application.</para></description>
+				</configOption>
+				<configOption name="hint_context" default="endpoint/context or 'default'">
+					<synopsis>The context in which to place hints.</synopsis>
+					<description>
+					<para>Ignored if <literal>hint_exten</literal> is not specified otherwise specifies the
+					context into which the dialplan hints will be placed.  If not specified,
+					defaults to the endpoint's context or <literal>default</literal> if that isn't
+					found.
+					</para></description>
+				</configOption>
+				<configOption name="hint_exten">
+					<synopsis>Extension to map a PJSIP hint to.</synopsis>
+					<description>
+					<para>Will create the following entry in <literal>hint_context</literal>:</para>
+					<para>   <literal>exten => <hint_exten>,hint,PJSIP/<wizard_id></literal></para>
+					<para> </para>
+					<para>Normal dialplan precedence rules apply so if there's already a hint for
+					this extension in <literal>hint_context</literal>, this one will be ignored.
+					For more information, visit: </para>
+					<para><literal>https://wiki.asterisk.org/wiki/display/AST/PJSIP+Configuration+Wizard</literal></para>
+					</description>
+				</configOption>
+				<configOption name="hint_application">
+					<synopsis>Application to call when 'hint_exten' is dialed.</synopsis>
+					<description>
+					<para>Ignored if <literal>hint_exten</literal> isn't specified otherwise
+					will create the following priority 1 extension in <literal>hint_context</literal>:</para>
+					<para>   <literal>exten => <hint_exten>,1,<hint_application></literal></para>
+					<para> </para>
+					<para>You can specify any valid extensions.conf application expression.</para>
+					<para>Examples: </para>
+					<para>   <literal>Dial(${HINT})</literal></para>
+					<para>   <literal>Gosub(stdexten,${EXTEN},1(${HINT}))</literal></para>
+					<para> </para>
+					<para>Any extensions.conf style variables specified are passed directly to the
+					dialplan.</para>
+					<para> </para>
+					<para>Normal dialplan precedence rules apply so if there's already a priority 1
+					application for this specific extension in <literal>hint_context</literal>,
+					this one will be ignored. For more information, visit: </para>
+					<para><literal>https://wiki.asterisk.org/wiki/display/AST/PJSIP+Configuration+Wizard</literal></para>
+					</description>
+				</configOption>
+				<configOption name="endpoint/*">
+					<synopsis>Variables to be passed directly to the endpoint.</synopsis>
+				</configOption>
+				<configOption name="aor/*">
+					<synopsis>Variables to be passed directly to the aor.</synopsis>
+					<description><para>If an aor/contact is explicitly defined then remote_hosts
+					will not be used to create contacts automatically.</para></description>
+				</configOption>
+				<configOption name="inbound_auth/*">
+					<synopsis>Variables to be passed directly to the inbound auth.</synopsis>
+				</configOption>
+				<configOption name="outbound_auth/*">
+					<synopsis>Variables to be passed directly to the outbound auth.</synopsis>
+				</configOption>
+				<configOption name="identify/*">
+					<synopsis>Variables to be passed directly to the identify.</synopsis>
+					<description><para>If an identify/match is explicitly defined then remote_hosts
+					will not be used to create matches automatically.</para></description>
+				</configOption>
+				<configOption name="registration/*">
+					<synopsis>Variables to be passed directly to the outbound registrations.</synopsis>
+				</configOption>
+				<configOption name="phoneprov/*">
+					<synopsis>Variables to be passed directly to the phoneprov object.</synopsis>
+					<description><para>
+					To activate phoneprov, at least phoneprov/MAC must be set.</para></description>
+				</configOption>
+			</configObject>
+		</configFile>
+	</configInfo>
+ ***/
+
+ /*! \brief Defines the maximum number of characters that can be added to a wizard id. */
+#define MAX_ID_SUFFIX 20
+
+#define BASE_REGISTRAR "res_pjsip_config_wizard"
+
+/*! \brief A generic char * vector definition. */
+AST_VECTOR(string_vector, char *);
+
+/*! \brief Keeps track of the sorcery wizard and last config for each object type */
+struct object_type_wizard {
+	struct ast_sorcery *sorcery;
+	struct ast_sorcery_wizard *wizard;
+	void *wizard_data;
+	struct ast_config *last_config;
+	char object_type[];
+};
+static AST_VECTOR(object_type_wizards, struct object_type_wizard *) object_type_wizards;
+
+/*! \brief Callbacks for vector deletes */
+#define NOT_EQUALS(a, b) (a != b)
+#define OTW_DELETE_CB(otw) ({ \
+	ast_config_destroy(otw->last_config); \
+	ast_free(otw); \
+})
+
+const static char *object_types[] = {"phoneprov", "registration", "identify", "endpoint", "aor", "auth", NULL};
+
+static int is_one_of(const char *needle, const char *haystack[])
+{
+	int i;
+	for (i = 0; haystack[i]; i++) {
+		if (!strcmp(needle, haystack[i])) {
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+/*! \brief Finds the otw for the object type */
+static struct object_type_wizard *find_wizard(const char *object_type)
+{
+	int idx;
+
+	for(idx = 0; idx < AST_VECTOR_SIZE(&object_type_wizards); idx++) {
+		struct object_type_wizard *otw = AST_VECTOR_GET(&object_type_wizards, idx);
+		if (!strcmp(otw->object_type, object_type)) {
+			return otw;
+		}
+	}
+
+	return NULL;
+}
+
+/*! \brief Creates a sorcery object and applies a variable list */
+static void *create_object(const struct ast_sorcery *sorcery,
+	const char *id, const char *type, struct ast_variable *vars)
+{
+	struct ast_sorcery_object *obj = ast_sorcery_alloc(sorcery, type, id);
+
+	if (!obj) {
+		ast_log(LOG_ERROR, "Unable to allocate an object of type '%s' with id '%s'.\n", type, id);
+		return NULL;
+	}
+
+	if (ast_sorcery_objectset_apply(sorcery, obj, vars)) {
+		ast_log(LOG_ERROR, "Unable to apply object type '%s' with id '%s'.  Check preceeding errors.\n", type, id);
+		ao2_ref(obj, -1);
+		return NULL;
+	}
+
+	return obj;
+}
+
+/*! \brief Finds the last variable in a list and tests it */
+static int is_variable_true(struct ast_variable *vars, const char *name)
+{
+	return ast_true(ast_variable_find_last_in_list(vars, name));
+}
+
+/*! \brief Appends a variable to the end of an existing list */
+static int variable_list_append(struct ast_variable **existing, const char *name, const char *value)
+{
+	struct ast_variable *new = ast_variable_new(name, value, "");
+
+	if (!new) {
+		ast_log(LOG_ERROR, "Unable to allocate memory for new variable '%s'.\n", name);
+		return -1;
+	}
+
+	ast_variable_list_append(existing, new);
+
+	return 0;
+}
+
+/*! \brief Appends a variable to the end of an existing list.  On failure, cause the calling
+ * function to return -1 */
+#define variable_list_append_return(existing, name, value) ({ \
+	struct ast_variable *new = ast_variable_new(name, value, ""); \
+	if (!new) { \
+		ast_log(LOG_ERROR, "Unable to allocate memory for new variable '%s'.\n", name); \
+		return -1; \
+	} \
+	ast_variable_list_append(existing, new); \
+})
+
+/*! \brief We need to strip off the prefix from the name of each variable
+ * so they're suitable for objectset_apply.
+ * I.E.  will transform outbound_auth/username to username.
+ */
+static struct ast_variable *get_object_variables(struct ast_variable *vars, char *prefix)
+{
+	struct ast_variable *return_vars = NULL;
+	struct ast_variable *v = vars;
+	int plen = strlen(prefix);
+
+	for(; v; v = v->next) {
+		if (ast_begins_with(v->name, prefix) && strlen(v->name) > plen) {
+			if (variable_list_append(&return_vars, v->name + plen, v->value)) {
+				ast_variables_destroy(return_vars);
+				return NULL;
+			}
+		}
+	}
+
+	return return_vars;
+}
+
+/* Don't call while holding context locks. */
+static int delete_extens(const char *context, const char *exten)
+{
+	struct pbx_find_info find_info = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
+
+	if (pbx_find_extension(NULL, NULL, &find_info, context, exten, PRIORITY_HINT, NULL, NULL, E_MATCH)) {
+		ast_context_remove_extension(context, exten, PRIORITY_HINT, BASE_REGISTRAR);
+	}
+
+	if (pbx_find_extension(NULL, NULL, &find_info, context, exten, 1, NULL, NULL, E_MATCH)) {
+		ast_context_remove_extension(context, exten, 1, BASE_REGISTRAR);
+	}
+
+	return 0;
+}
+
+static int add_extension(struct ast_context *context, const char *exten,
+	int priority, const char *application)
+{
+	struct pbx_find_info find_info = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
+	struct ast_exten *existing_exten;
+	char *data = NULL;
+	char *app = NULL;
+	void *free_ptr = NULL;
+	char *paren;
+	const char *context_name;
+
+	if (!context || ast_strlen_zero(exten) || ast_strlen_zero(application)) {
+		return -1;
+	}
+
+	/* The incoming application has to be split into the app name and the
+	 * arguments (data).  The app name can be any storage type as add_extension
+	 * copies it into its own buffer.  Data however, needs to be dynamically
+	 * allocated and a free function provided.
+	 */
+
+	paren = strchr(application, '(');
+	if (!paren) {
+		app = (char *)application;
+	} else {
+		app = ast_strdupa(application);
+		app[paren - application] = '\0';
+		data = ast_strdup(paren + 1);
+		if (!data) {
+			return -1;
+		}
+		data[strlen(data) - 1] = '\0';
+		free_ptr = ast_free_ptr;
+		if (ast_strlen_zero(app) || ast_strlen_zero(data)) {
+			ast_free(data);
+			return -1;
+		}
+	}
+
+	/* Don't disturb existing, exact-match, entries. */
+	context_name = ast_get_context_name(context);
+	if ((existing_exten = pbx_find_extension(NULL, NULL, &find_info, context_name, exten,
+		priority, NULL, NULL, E_MATCH))) {
+		const char *existing_app = ast_get_extension_app(existing_exten);
+		const char *existing_data = ast_get_extension_app_data(existing_exten);
+		if (!strcmp(existing_app, app)
+			&& !strcmp(existing_data ? existing_data : "", data ? data : "")) {
+			ast_free(data);
+			return 0;
+		}
+
+		ast_context_remove_extension2(context, exten, priority, BASE_REGISTRAR, 1);
+	}
+
+	if (ast_add_extension2_nolock(context, 0, exten, priority, NULL, NULL,
+			app, data, free_ptr, BASE_REGISTRAR)) {
+		ast_free(data);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int add_hints(const char *context, const char *exten, const char *application, const char *id)
+{
+	struct ast_context *hint_context;
+	char *hint_device;
+
+	hint_device = ast_alloca(strlen("PJSIP/") + strlen(id) + 1);
+	sprintf(hint_device, "PJSIP/%s", id);
+
+	/* We need the contexts list locked to safely be able to both read and lock the specific context within */
+	if (ast_wrlock_contexts()) {
+		ast_log(LOG_ERROR, "Failed to lock the contexts list.\n");
+		return -1;
+	}
+
+	if (!(hint_context = ast_context_find_or_create(NULL, NULL, context, BASE_REGISTRAR))) {
+		ast_log(LOG_ERROR, "Unable to find or create hint context '%s'\n", context);
+		if (ast_unlock_contexts()) {
+			ast_assert(0);
+		}
+		return -1;
+	}
+
+	/* Transfer the all-contexts lock to the specific context */
+	if (ast_wrlock_context(hint_context)) {
+		ast_unlock_contexts();
+		ast_log(LOG_ERROR, "failed to obtain write lock on context\n");
+		return -1;
+	}
+	ast_unlock_contexts();
+
+	if (add_extension(hint_context, exten, PRIORITY_HINT, hint_device)) {
+		ast_log(LOG_ERROR, "Failed to add hint '%s@%s' to the PBX.\n",
+		        exten, context);
+	}
+
+	if (!ast_strlen_zero(application)) {
+		if (add_extension(hint_context, exten, 1, application)) {
+			ast_log(LOG_ERROR, "Failed to add hint '%s@%s' to the PBX.\n",
+			        exten, context);
+		}
+	} else {
+		ast_context_remove_extension2(hint_context, exten, 1, BASE_REGISTRAR, 1);
+	}
+
+	ast_unlock_context(hint_context);
+
+	return 0;
+}
+
+static int handle_auth(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
+	struct ast_category *wiz, char *direction)
+{
+	struct ast_variable *wizvars = ast_category_first(wiz);
+	struct ast_sorcery_object *obj = NULL;
+	const char *id = ast_category_get_name(wiz);
+	char new_id[strlen(id) + MAX_ID_SUFFIX];
+	char prefix[strlen(direction) + strlen("_auth/") + 1];
+	char *test_variable = NULL;
+	RAII_VAR(struct ast_variable *, vars, NULL, ast_variables_destroy);
+
+	snprintf(prefix, sizeof(prefix), "%s_auth/", direction);
+	vars = get_object_variables(wizvars, prefix);
+
+	if (!strcmp(direction, "outbound")) {
+		snprintf(new_id, sizeof(new_id), "%s-oauth", id);
+		test_variable = "sends_auth";
+	} else {
+		snprintf(new_id, sizeof(new_id), "%s-iauth", id);
+		test_variable = "accepts_auth";
+	}
+
+	if (is_variable_true(wizvars, test_variable)) {
+		if (!ast_variable_find_last_in_list(vars, "username")) {
+			ast_log(LOG_ERROR,
+				"Wizard '%s' must have '%s_auth/username' if it %s.\n", id, direction, test_variable);
+			return -1;
+		}
+	} else {
+		/* Delete auth if sends or accepts is now false. */
+		obj = otw->wizard->retrieve_id(sorcery, otw->wizard_data, "auth", new_id);
+		if (obj) {
+			otw->wizard->delete(sorcery, otw->wizard_data, obj);
+			ao2_ref(obj, -1);
+		}
+		return 0;
+	}
+
+	variable_list_append_return(&vars, "@pjsip_wizard", id);
+
+	/* If the user set auth_type, don't override it. */
+	if (!ast_variable_find_last_in_list(vars, "auth_type")) {
+		variable_list_append_return(&vars, "auth_type", "userpass");
+	}
+
+	obj = create_object(sorcery, new_id, "auth", vars);
+	if (!obj) {
+		return -1;
+	}
+
+	if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
+		otw->wizard->create(sorcery, otw->wizard_data, obj);
+	}
+	ao2_ref(obj, -1);
+
+	return 0;
+}
+
+static int handle_auths(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
+	struct ast_category *wiz)
+{
+	int rc;
+
+	if ((rc = handle_auth(sorcery, otw, wiz, "outbound"))) {
+		return rc;
+	}
+
+	return handle_auth(sorcery, otw, wiz, "inbound");
+}
+
+static int handle_aor(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
+	struct ast_category *wiz, struct string_vector *remote_hosts_vector)
+{
+	struct ast_variable *wizvars = ast_category_first(wiz);
+	struct ast_sorcery_object *obj = NULL;
+	const char *id = ast_category_get_name(wiz);
+	const char *contact_pattern;
+	int host_count = AST_VECTOR_SIZE(remote_hosts_vector);
+	RAII_VAR(struct ast_variable *, vars, get_object_variables(wizvars, "aor/"), ast_variables_destroy);
+
+	variable_list_append(&vars, "@pjsip_wizard", id);
+
+	/* If the user explicitly specified an aor/contact, don't use remote hosts. */
+	if (!ast_variable_find_last_in_list(vars, "contact")) {
+		if (!(contact_pattern = ast_variable_find_last_in_list(wizvars, "contact_pattern"))) {
+			contact_pattern = "sip:${REMOTE_HOST}";
+		}
+
+		if (host_count > 0 && !ast_strlen_zero(contact_pattern)) {
+			int host_counter;
+
+			/* ast_str_substitute_variables operate on a varshead list so we have
+			 * to create one to hold the REPORT_HOST substitution, do the substitution,
+			 * then append the result to the ast_variable list.
+			 */
+			for (host_counter = 0; host_counter < host_count; host_counter++) {
+				RAII_VAR(struct ast_str *, new_str, ast_str_create(64), ast_free);
+				RAII_VAR(struct varshead *, subst_vars, ast_var_list_create(), ast_var_list_destroy);
+				struct ast_var_t *var = ast_var_assign("REMOTE_HOST",
+					AST_VECTOR_GET(remote_hosts_vector, host_counter));
+
+				AST_VAR_LIST_INSERT_TAIL(subst_vars, var);
+				ast_str_substitute_variables_varshead(&new_str, 0, subst_vars,
+					contact_pattern);
+
+				variable_list_append_return(&vars, "contact", ast_str_buffer(new_str));
+			}
+		}
+	}
+
+	obj = create_object(sorcery, id, "aor", vars);
+	if (!obj) {
+		return -1;
+	}
+
+	if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
+		otw->wizard->create(sorcery, otw->wizard_data, obj);
+	}
+	ao2_ref(obj, -1);
+
+	return 0;
+}
+
+static int handle_endpoint(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
+	struct ast_category *wiz)
+{
+	struct ast_variable *wizvars = ast_category_first(wiz);
+	struct ast_sorcery_object *obj = NULL;
+	const char *id = ast_category_get_name(wiz);
+	const char *transport = ast_variable_find_last_in_list(wizvars, "transport");
+	const char *hint_context = hint_context = ast_variable_find_last_in_list(wizvars, "hint_context");
+	const char *hint_exten = ast_variable_find_last_in_list(wizvars, "hint_exten");
+	const char *hint_application= ast_variable_find_last_in_list(wizvars, "hint_application");
+	char new_id[strlen(id) + MAX_ID_SUFFIX];
+	RAII_VAR(struct ast_variable *, vars, get_object_variables(wizvars, "endpoint/"), ast_variables_destroy);
+
+	variable_list_append_return(&vars, "@pjsip_wizard", id);
+	variable_list_append_return(&vars, "aors", id);
+
+	if (ast_strlen_zero(hint_context)) {
+		hint_context = ast_variable_find_last_in_list(vars, "context");
+	}
+
+	if (ast_strlen_zero(hint_context)) {
+		hint_context = "default";
+	}
+
+	if (!ast_strlen_zero(hint_exten)) {
+		/* These are added so we can find and delete the hints when the endpoint gets deleted */
+		variable_list_append_return(&vars, "@hint_context", hint_context);
+		variable_list_append_return(&vars, "@hint_exten", hint_exten);
+	}
+
+	if (!ast_strlen_zero(transport)) {
+		variable_list_append_return(&vars, "transport", transport);
+	}
+
+	if (is_variable_true(wizvars, "sends_auth")) {
+		snprintf(new_id, sizeof(new_id), "%s-oauth", id);
+		variable_list_append_return(&vars, "outbound_auth", new_id);
+	}
+
+	if (is_variable_true(wizvars, "accepts_auth")) {
+		snprintf(new_id, sizeof(new_id), "%s-iauth", id);
+		variable_list_append_return(&vars, "auth", new_id);
+	}
+
+	obj = create_object(sorcery, id, "endpoint", vars);
+	if (!obj) {
+		return -1;
+	}
+
+	if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
+		otw->wizard->create(sorcery, otw->wizard_data, obj);
+	}
+	ao2_ref(obj, -1);
+
+	if (!ast_strlen_zero(hint_exten)) {
+		if (is_variable_true(wizvars, "has_hint")) {
+			add_hints(hint_context, hint_exten, hint_application, id);
+		} else {
+			delete_extens(hint_context, hint_exten);
+		}
+	}
+
+	return 0;
+}
+
+static int handle_identify(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
+	struct ast_category *wiz, struct string_vector *remote_hosts_vector)
+{
+	struct ast_variable *wizvars = ast_category_first(wiz);
+	struct ast_sorcery_object *obj = NULL;
+	const char *id = ast_category_get_name(wiz);
+	char new_id[strlen(id) + MAX_ID_SUFFIX];
+	int host_count = AST_VECTOR_SIZE(remote_hosts_vector);
+	int host_counter;
+	RAII_VAR(struct ast_variable *, vars, get_object_variables(wizvars, "identify/"), ast_variables_destroy);
+
+	snprintf(new_id, sizeof(new_id), "%s-identify", id);
+
+	/* If accepting registrations, we don't need an identify. */
+	if (is_variable_true(wizvars, "accepts_registrations")) {
+		/* If one exists, delete it. */
+		obj = otw->wizard->retrieve_id(sorcery, otw->wizard_data, "identify", new_id);
+		if (obj) {
+			otw->wizard->delete(sorcery, otw->wizard_data, obj);
+			ao2_ref(obj, -1);
+		}
+		return 0;
+	}
+
+	if (!host_count) {
+		ast_log(LOG_ERROR,
+			"Wizard '%s' must have 'remote_hosts' if it doesn't accept registrations.\n", id);
+		return -1;
+	}
+
+	variable_list_append_return(&vars, "endpoint", id);
+	variable_list_append_return(&vars, "@pjsip_wizard", id);
+
+	if (!ast_variable_find_last_in_list(vars, "match")) {
+		for (host_counter = 0; host_counter < host_count; host_counter++) {
+			char *rhost = AST_VECTOR_GET(remote_hosts_vector, host_counter);
+			char host[strlen(rhost) + 1];
+			char *colon;
+
+			/* If there's a :port specified, we have to remove it. */
+			strcpy(host, rhost); /* Safe */
+			colon = strchr(host, ':');
+			if (colon) {
+				*colon = '\0';
+			}
+
+			variable_list_append_return(&vars, "match", host);
+		}
+	}
+
+	obj = create_object(sorcery, new_id, "identify", vars);
+	if (!obj) {
+		return -1;
+	}
+
+	if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
+		otw->wizard->create(sorcery, otw->wizard_data, obj);
+	}
+	ao2_ref(obj, -1);
+
+	return 0;
+}
+
+static int handle_phoneprov(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
+	struct ast_category *wiz)
+{
+	struct ast_variable *wizvars = ast_category_first(wiz);
+	struct ast_sorcery_object *obj = NULL;
+	const char *id = ast_category_get_name(wiz);
+	char new_id[strlen(id) + MAX_ID_SUFFIX];
+	RAII_VAR(struct ast_variable *, vars, get_object_variables(wizvars, "phoneprov/"), ast_variables_destroy);
+
+	snprintf(new_id, sizeof(new_id), "%s-phoneprov", id);
+
+	if (!is_variable_true(wizvars, "has_phoneprov")) {
+		obj = otw->wizard->retrieve_id(sorcery, otw->wizard_data, "phoneprov", new_id);
+		if (obj) {
+			otw->wizard->delete(sorcery, otw->wizard_data, obj);
+			ao2_ref(obj, -1);
+		}
+		return 0;
+	}
+
+	if (!ast_variable_find_last_in_list(wizvars, "phoneprov/MAC")) {
+		ast_log(LOG_ERROR,
+			"Wizard '%s' must have 'phoneprov/MAC' if it has_phoneprov.\n", id);
+		return -1;
+	}
+
+	variable_list_append_return(&vars, "endpoint", id);
+	variable_list_append_return(&vars, "@pjsip_wizard", id);
+
+	obj = create_object(sorcery, new_id, "phoneprov", vars);
+	if (!obj) {
+		return -1;
+	}
+
+	if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
+		otw->wizard->create(sorcery, otw->wizard_data, obj);
+	}
+	ao2_ref(obj, -1);
+
+	return 0;
+}
+
+static int delete_existing_cb(void *obj, void *arg, int flags)
+{
+	struct object_type_wizard *otw = arg;
+
+	if (!strcmp(otw->object_type, "endpoint")) {
+		const char *context = ast_sorcery_object_get_extended(obj, "hint_context");
+		const char *exten = ast_sorcery_object_get_extended(obj, "hint_exten");
+		if (!ast_strlen_zero(context) && !ast_strlen_zero(exten)) {
+			delete_extens(context, exten);
+		}
+	}
+
+	otw->wizard->delete(otw->sorcery, otw->wizard_data, obj);
+
+	return CMP_MATCH;
+}
+
+static int handle_registrations(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
+	struct ast_category *wiz, struct string_vector *remote_hosts_vector)
+{
+	struct ast_variable *search;
+	struct ast_variable *wizvars = ast_category_first(wiz);
+	const char *id = ast_category_get_name(wiz);
+	const char *server_uri_pattern;
+	const char *client_uri_pattern;
+	const char *transport = ast_variable_find_last_in_list(wizvars, "transport");
+	const char *username;
+	char new_id[strlen(id) + MAX_ID_SUFFIX];
+	int host_count = AST_VECTOR_SIZE(remote_hosts_vector);
+	int host_counter;
+	RAII_VAR(struct ast_variable *, vars, get_object_variables(wizvars, "registration/"), ast_variables_destroy);
+	RAII_VAR(struct ao2_container *, existing,
+		ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, NULL, NULL), ao2_cleanup);
+
+	if (!existing) {
+		return -1;
+	}
+
+	/* Find any existing registrations. */
+	search = ast_variable_new("@pjsip_wizard", id, "");
+	if (!search) {
+		return -1;
+	}
+
+	otw->wizard->retrieve_multiple(sorcery, otw->wizard_data, "registration", existing, search);
+	ast_variables_destroy(search);
+
+	/* If not sending registrations, delete ALL existing registrations for this wizard. */
+	if (!is_variable_true(wizvars, "sends_registrations")) {
+		if (ao2_container_count(existing) > 0) {
+			ao2_callback(existing, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, delete_existing_cb, otw);
+		}
+		return 0;
+	}
+
+	if (!host_count) {
+		ast_log(LOG_ERROR, "Wizard '%s' must have 'remote_hosts' if it sends registrations.\n", id);
+		return -1;
+	}
+
+	variable_list_append_return(&vars, "@pjsip_wizard", id);
+
+	if (!(server_uri_pattern = ast_variable_find_last_in_list(wizvars, "server_uri_pattern"))) {
+		server_uri_pattern = "sip:${REMOTE_HOST}";
+	}
+
+	if (!(client_uri_pattern = ast_variable_find_last_in_list(wizvars, "client_uri_pattern"))) {
+		client_uri_pattern = "sip:${USERNAME}@${REMOTE_HOST}";
+	}
+
+	if(is_variable_true(wizvars, "sends_auth")) {
+		username = ast_variable_find_last_in_list(wizvars, "outbound_auth/username");
+	} else {
+		username = id;
+	}
+
+
+	/* Unlike aor and identify, we need to create a separate registration object
+	 * for each remote host.
+	 */
+	for (host_counter = 0; host_counter < host_count; host_counter++) {
+		struct ast_var_t *rh = ast_var_assign("REMOTE_HOST",
+			AST_VECTOR_GET(remote_hosts_vector, host_counter));
+		struct ast_var_t *un = ast_var_assign("USERNAME", username);
+		struct ast_sorcery_object *obj;
+		RAII_VAR(struct ast_str *, uri, ast_str_create(64), ast_free);
+		RAII_VAR(struct varshead *, subst_vars, ast_var_list_create(), ast_var_list_destroy);
+		RAII_VAR(struct ast_variable *, registration_vars, vars ? ast_variables_dup(vars) : NULL, ast_variables_destroy);
+
+		AST_VAR_LIST_INSERT_TAIL(subst_vars, rh);
+		AST_VAR_LIST_INSERT_TAIL(subst_vars, un);
+
+		if (!ast_strlen_zero(server_uri_pattern)) {
+			ast_str_substitute_variables_varshead(&uri, 0, subst_vars,
+				server_uri_pattern);
+			variable_list_append_return(&registration_vars, "server_uri", ast_str_buffer(uri));
+		}
+
+		if (!ast_strlen_zero(client_uri_pattern)) {
+			ast_str_reset(uri);
+			ast_str_substitute_variables_varshead(&uri, 0, subst_vars,
+				client_uri_pattern);
+			variable_list_append_return(&registration_vars, "client_uri", ast_str_buffer(uri));
+		}
+
+		if (is_variable_true(wizvars, "sends_auth")) {
+			snprintf(new_id, sizeof(new_id), "%s-oauth", id);
+			variable_list_append_return(&registration_vars, "outbound_auth", new_id);
+		}
+
+		if (!ast_strlen_zero(transport)) {
+			variable_list_append_return(&registration_vars, "transport", transport);
+		}
+
+		snprintf(new_id, sizeof(new_id), "%s-reg-%d", id, host_counter);
+
+		obj = create_object(sorcery, new_id, "registration", registration_vars);
+		if (!obj) {
+			return -1;
+		}
+
+		if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
+			otw->wizard->create(sorcery, otw->wizard_data, obj);
+		}
+		ao2_ref(obj, -1);
+
+		/* Unlink it from the 'existing' container.  Any left will be deleted from
+		 * sorcery.  If it wasn't in the existing container, no harm.
+		 */
+		ao2_callback(existing, OBJ_NODATA | OBJ_UNLINK | OBJ_SEARCH_KEY, ast_sorcery_object_id_compare, new_id);
+	}
+
+	/* If there are any excess registrations, delete them. */
+	if (ao2_container_count(existing) > 0) {
+		ao2_callback(existing, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, delete_existing_cb, otw);
+	}
+
+	return 0;
+}
+
+static int wizard_apply_handler(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
+	struct ast_category *wiz)
+{
+	struct ast_variable *wizvars = ast_category_first(wiz);
+	struct string_vector remote_hosts_vector;
+	const char *remote_hosts;
+	int rc = -1;
+
+	AST_VECTOR_INIT(&remote_hosts_vector, 16);
+	remote_hosts = ast_variable_find_last_in_list(wizvars, "remote_hosts");
+
+	if (!ast_strlen_zero(remote_hosts)) {
+		char *host;
+		char *hosts = ast_strdupa(remote_hosts);
+
+		while ((host = ast_strsep(&hosts, ',', AST_STRSEP_TRIM))) {
+			AST_VECTOR_APPEND(&remote_hosts_vector, ast_strdup(host));
+		}
+	}
+
+	ast_debug(4, "%s handler starting.\n", otw->object_type);
+
+	if (!strcmp(otw->object_type, "auth")) {
+		rc = handle_auths(sorcery, otw, wiz);
+	} else if (!strcmp(otw->object_type, "aor")) {
+		rc = handle_aor(sorcery, otw, wiz, &remote_hosts_vector);
+	} else if (!strcmp(otw->object_type, "endpoint")) {
+		rc = handle_endpoint(sorcery, otw, wiz);
+	} else if (!strcmp(otw->object_type, "identify")) {
+		rc = handle_identify(sorcery, otw, wiz, &remote_hosts_vector);
+	} else if (!strcmp(otw->object_type, "phoneprov")) {
+		rc = handle_phoneprov(sorcery, otw, wiz);
+	} else if (!strcmp(otw->object_type, "registration")) {
+		rc = handle_registrations(sorcery, otw, wiz, &remote_hosts_vector);
+	}
+
+	AST_VECTOR_REMOVE_CMP_UNORDERED(&remote_hosts_vector, NULL, NOT_EQUALS, ast_free);
+	AST_VECTOR_FREE(&remote_hosts_vector);
+
+	ast_debug(4, "%s handler complete.  rc: %d\n", otw->object_type, rc);
+
+	return rc;
+}
+
+/*
+ * Everything below are the sorcery observers.
+ */
+static void instance_created_observer(const char *name, struct ast_sorcery *sorcery);
+static void instance_destroying_observer(const char *name, struct ast_sorcery *sorcery);
+static void object_type_loaded_observer(const char *name,
+	const struct ast_sorcery *sorcery, const char *object_type, int reloaded);
+static void wizard_mapped_observer(const char *name, struct ast_sorcery *sorcery,
+	const char *object_type, struct ast_sorcery_wizard *wizard,
+	const char *wizard_args, void *wizard_data);
+static void object_type_registered_observer(const char *name,
+	struct ast_sorcery *sorcery, const char *object_type);
+
+const static struct ast_sorcery_global_observer global_observer = {
+	.instance_created = instance_created_observer,
+	.instance_destroying = instance_destroying_observer,
+};
+
+struct ast_sorcery_instance_observer observer = {
+	.wizard_mapped = wizard_mapped_observer,
+	.object_type_registered = object_type_registered_observer,
+	.object_type_loaded = object_type_loaded_observer,
+};
+
+/*! \brief Called after an object type is loaded/reloaded */
+static void object_type_loaded_observer(const char *name,
+	const struct ast_sorcery *sorcery, const char *object_type, int reloaded)
+{
+	struct ast_category *category = NULL;
+	struct object_type_wizard *otw = NULL;
+	char *filename = "pjsip_wizard.conf";
+	struct ast_flags flags = { 0 };
+	struct ast_config *cfg;
+
+	if (!strstr("auth aor endpoint identify registration phoneprov", object_type)) {
+		/* Not interested. */
+		return;
+	}
+
+	otw = find_wizard(object_type);
+	if (!otw) {
+		ast_log(LOG_ERROR, "There was no wizard for object type '%s'\n", object_type);
+		return;
+	}
+
+	if (reloaded && otw->last_config) {
+		flags.flags = CONFIG_FLAG_FILEUNCHANGED;
+	}
+
+	cfg = ast_config_load2(filename, object_type, flags);
+
+	if (!cfg) {
+		ast_log(LOG_ERROR, "Unable to load config file '%s'\n", filename);
+		return;
+	} else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
+		ast_debug(2, "Config file '%s' was unchanged for '%s'.\n", filename, object_type);
+		return;
+	} else if (cfg == CONFIG_STATUS_FILEINVALID) {
+		ast_log(LOG_ERROR, "Contents of config file '%s' are invalid and cannot be parsed\n", filename);
+		return;
+	}
+
+	while ((category = ast_category_browse_filtered(cfg, NULL, category, "type=^wizard$"))) {
+		const char *id = ast_category_get_name(category);
+		struct ast_category *last_cat = NULL;
+		struct ast_variable *change_set = NULL;
+
+		if (otw->last_config) {
+			last_cat = ast_category_get(otw->last_config, id, "type=^wizard$");
+			ast_sorcery_changeset_create(ast_category_first(category), ast_category_first(last_cat), &change_set);
+			if (last_cat) {
+				ast_category_delete(otw->last_config, last_cat);
+			}
+		}
+
+		if (!last_cat || change_set) {
+			ast_variables_destroy(change_set);
+			ast_debug(3, "%s: %s(s) for wizard '%s'\n", reloaded ? "Reload" : "Load", object_type, id);
+			if (wizard_apply_handler(sorcery, otw, category)) {
+				ast_log(LOG_ERROR, "Unable to create objects for wizard '%s'\n", id);
+			}
+		}
+	}
+
+	if (!otw->last_config) {
+		otw->last_config = cfg;
+		return;
+	}
+
+	/* Only wizards that weren't in the new config are left in last_config now so we need to delete
+	 * all objects belonging to them.
+	 */
+	category = NULL;
+	while ((category = ast_category_browse_filtered(otw->last_config, NULL, category, "type=^wizard$"))) {
+		const char *id = ast_category_get_name(category);
+		struct ast_variable *search;
+		RAII_VAR(struct ao2_container *, existing,
+			ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, NULL, NULL), ao2_cleanup);
+
+		if (!existing) {
+			ast_log(LOG_ERROR, "Unable to allocate temporary container.\n");
+			break;
+		}
+
+		search = ast_variable_new("@pjsip_wizard", id, "");
+		if (!search) {
+			ast_log(LOG_ERROR, "Unable to allocate memory for vaiable '@pjsip_wizard'.\n");
+			break;
+		}
+		otw->wizard->retrieve_multiple(sorcery, otw->wizard_data, object_type, existing, search);
+		ast_variables_destroy(search);
+
+		if (ao2_container_count(existing) > 0) {
+			ast_debug(3, "Delete on %s: %d %s(s) for wizard: %s\n",
+				reloaded ? "Reload" : "Load", ao2_container_count(existing), object_type, id);
+			ao2_callback(existing, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE,
+				delete_existing_cb, otw);
+		}
+	}
+
+	ast_config_destroy(otw->last_config);
+	otw->last_config = cfg;
+}
+
+/*! \brief When each wizard is mapped, save it off to the vector. */
+static void wizard_mapped_observer(const char *name, struct ast_sorcery *sorcery,
+	const char *object_type, struct ast_sorcery_wizard *wizard,
+	const char *wizard_args, void *wizard_data)
+{
+	struct object_type_wizard *otw;
+
+	if (!is_one_of(object_type, object_types)) {
+		/* Not interested. */
+		return;
+	}
+
+	/* We're only interested in memory wizards with the pjsip_wizard tag. */
+	if (wizard_args && !strcmp(wizard_args, "pjsip_wizard")) {
+		otw = ast_malloc(sizeof(*otw) + strlen(object_type) + 1);
+		otw->sorcery = sorcery;
+		otw->wizard = wizard;
+		otw->wizard_data = wizard_data;
+		otw->last_config = NULL;
+		strcpy(otw->object_type, object_type); /* Safe */
+		AST_VECTOR_APPEND(&object_type_wizards, otw);
+		ast_debug(1, "Wizard mapped for object_type '%s'\n", object_type);
+	}
+}
+
+/*! \brief When each object type is registered, map a memory wizard to it. */
+static void object_type_registered_observer(const char *name,
+	struct ast_sorcery *sorcery, const char *object_type)
+{
+	if (is_one_of(object_type, object_types)) {
+		ast_sorcery_apply_wizard_mapping(sorcery, object_type, "memory", "pjsip_wizard", 0);
+	}
+}
+
+/*! \brief When the res_pjsip instance is created, add an observer to it and initialize the wizard vector.
+ * Also, bump the module's ref count so it can't be unloaded before the sorcery instance is
+ * destroyed.
+ */
+static void instance_created_observer(const char *name, struct ast_sorcery *sorcery)
+{
+	if (strcmp(name, "res_pjsip")) {
+		return;
+	}
+	ast_module_ref(ast_module_info->self);
+	ast_sorcery_instance_observer_add(sorcery, &observer);
+}
+
+/*! \brief When the res_pjsip instance is destroyed, remove the observer
+ * and unref the module.  This should then allow this module to unload cleanly.
+ */
+static void instance_destroying_observer(const char *name, struct ast_sorcery *sorcery)
+{
+	if (strcmp(name, "res_pjsip")) {
+		return;
+	}
+
+	ast_sorcery_instance_observer_remove(sorcery, &observer);
+	ast_module_unref(ast_module_info->self);
+}
+
+static int load_module(void)
+{
+	AST_VECTOR_INIT(&object_type_wizards, 12);
+	ast_sorcery_global_observer_add(&global_observer);
+
+	return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+	ast_sorcery_global_observer_remove(&global_observer);
+	AST_VECTOR_REMOVE_CMP_UNORDERED(&object_type_wizards, NULL, NOT_EQUALS, OTW_DELETE_CB);
+	AST_VECTOR_FREE(&object_type_wizards);
+
+	return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "PJSIP Config Wizard",
+		.support_level = AST_MODULE_SUPPORT_CORE,
+		.load = load_module,
+		.unload = unload_module,
+		.load_pri = AST_MODPRI_REALTIME_DRIVER,
+		);
diff --git a/res/res_pjsip_dialog_info_body_generator.c b/res/res_pjsip_dialog_info_body_generator.c
index d9725f4..48ac60f 100644
--- a/res/res_pjsip_dialog_info_body_generator.c
+++ b/res/res_pjsip_dialog_info_body_generator.c
@@ -163,14 +163,13 @@ static void dialog_info_to_string(void *body, struct ast_str **str)
 	int size;
 
 	do {
-		size = pj_xml_print(dialog_info, ast_str_buffer(*str), ast_str_size(*str), PJ_TRUE);
-		if (size == AST_PJSIP_XML_PROLOG_LEN) {
+		size = pj_xml_print(dialog_info, ast_str_buffer(*str), ast_str_size(*str) - 1, PJ_TRUE);
+		if (size <= AST_PJSIP_XML_PROLOG_LEN) {
 			ast_str_make_space(str, ast_str_size(*str) * 2);
 			++growths;
 		}
-	} while (size == AST_PJSIP_XML_PROLOG_LEN && growths < MAX_STRING_GROWTHS);
-
-	if (size == AST_PJSIP_XML_PROLOG_LEN) {
+	} while (size <= AST_PJSIP_XML_PROLOG_LEN && growths < MAX_STRING_GROWTHS);
+	if (size <= AST_PJSIP_XML_PROLOG_LEN) {
 		ast_log(LOG_WARNING, "dialog-info+xml body text too large\n");
 		return;
 	}
diff --git a/res/res_pjsip_diversion.c b/res/res_pjsip_diversion.c
index a4ac157..4d9aca4 100644
--- a/res/res_pjsip_diversion.c
+++ b/res/res_pjsip_diversion.c
@@ -117,7 +117,9 @@ static void set_redirecting_value(char **dst, const pj_str_t *src)
 {
 	ast_free(*dst);
 	*dst = ast_malloc(pj_strlen(src) + 1);
-	ast_copy_pj_str(*dst, src, pj_strlen(src) + 1);
+	if (*dst) {
+		ast_copy_pj_str(*dst, src, pj_strlen(src) + 1);
+	}
 }
 
 static void set_redirecting_id(pjsip_name_addr *name_addr, struct ast_party_id *data,
@@ -248,6 +250,7 @@ static void add_diversion_header(pjsip_tx_data *tdata, struct ast_party_redirect
 	pjsip_name_addr *name_addr;
 	pjsip_sip_uri *uri;
 	pjsip_param *param;
+	pjsip_fromto_hdr *old_hdr;
 
 	struct ast_party_id *id = &data->from;
 	pjsip_uri *base = PJSIP_MSG_FROM_HDR(tdata->msg)->uri;
@@ -273,6 +276,10 @@ static void add_diversion_header(pjsip_tx_data *tdata, struct ast_party_redirect
 	pj_list_insert_before(&hdr->other_param, param);
 
 	hdr->uri = (pjsip_uri *) name_addr;
+	old_hdr = pjsip_msg_find_hdr_by_name(tdata->msg, &diversion_name, NULL);
+	if (old_hdr) {
+		pj_list_erase(old_hdr);
+	}
 	pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)hdr);
 }
 
diff --git a/res/res_pjsip_dlg_options.c b/res/res_pjsip_dlg_options.c
new file mode 100644
index 0000000..564c51f
--- /dev/null
+++ b/res/res_pjsip_dlg_options.c
@@ -0,0 +1,107 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2015, Digium, Inc.
+ *
+ * Yaron Nahum <nachum.yaron at gmail.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*** MODULEINFO
+	<depend>pjproject</depend>
+	<depend>res_pjsip</depend>
+	<depend>res_pjsip_session</depend>
+	<support_level>core</support_level>
+***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <pjsip.h>
+#include <pjsip_ua.h>
+#include <pjlib.h>
+
+#include "asterisk/module.h"
+#include "asterisk/res_pjsip.h"
+#include "asterisk/res_pjsip_session.h"
+
+#define DEFAULT_LANGUAGE "en"
+#define DEFAULT_ENCODING "text/plain"
+
+static int options_incoming_request(struct ast_sip_session *session, pjsip_rx_data *rdata)
+{
+	pjsip_tx_data *tdata;
+        pj_status_t status;
+	const pjsip_hdr *hdr;
+	pjsip_endpoint *endpt = ast_sip_get_pjsip_endpoint();
+
+	status = pjsip_dlg_create_response(session->inv_session->dlg, rdata, 200, NULL,&tdata);
+	if (status != PJ_SUCCESS) {
+		ast_log(LOG_ERROR, "Unable to create response (%d)\n", status);
+		return status;
+	}
+
+	/* Add appropriate headers */
+	if ((hdr = pjsip_endpt_get_capability(endpt, PJSIP_H_ACCEPT, NULL))) {
+		pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, hdr));
+	}
+	if ((hdr = pjsip_endpt_get_capability(endpt, PJSIP_H_ALLOW, NULL))) {
+		pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, hdr));
+	}
+	if ((hdr = pjsip_endpt_get_capability(endpt, PJSIP_H_SUPPORTED, NULL))) {
+		pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, hdr));
+	}
+
+	/*
+	 * XXX TODO: pjsip doesn't care a lot about either of these headers -
+	 * while it provides specific methods to create them, they are defined
+	 * to be the standard string header creation. We never did add them
+	 * in chan_sip, although RFC 3261 says they SHOULD. Hard coded here.
+	*/
+	ast_sip_add_header(tdata, "Accept-Encoding", DEFAULT_ENCODING);
+	ast_sip_add_header(tdata, "Accept-Language", DEFAULT_LANGUAGE);
+
+	status = pjsip_dlg_send_response(session->inv_session->dlg, pjsip_rdata_get_tsx(rdata), tdata);
+	if (status != PJ_SUCCESS) {
+		ast_log(LOG_ERROR, "Unable to send response (%d)\n", status);
+	}
+
+	return status;
+}
+
+static struct ast_sip_session_supplement  dlg_options_supplement = {
+	.method = "OPTIONS",
+	.incoming_request = options_incoming_request,
+};
+
+static int load_module(void)
+{
+	CHECK_PJSIP_MODULE_LOADED();
+
+	if (ast_sip_session_register_supplement(&dlg_options_supplement)) {
+		return AST_MODULE_LOAD_DECLINE;
+	}
+	return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+	ast_sip_session_unregister_supplement(&dlg_options_supplement);
+	return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "SIP OPTIONS in dialog handler",
+	.load = load_module,
+	.unload = unload_module,
+	.load_pri = AST_MODPRI_APP_DEPEND,
+);
diff --git a/res/res_pjsip_dtmf_info.c b/res/res_pjsip_dtmf_info.c
index b0a6649..7b52250 100644
--- a/res/res_pjsip_dtmf_info.c
+++ b/res/res_pjsip_dtmf_info.c
@@ -89,7 +89,13 @@ static int dtmf_info_incoming_request(struct ast_sip_session *session, struct pj
 	char event = '\0';
 	unsigned int duration = 100;
 
-	char is_dtmf = is_media_type(rdata, "dtmf");
+	char is_dtmf;
+
+	if (!session->channel) {
+		return 0;
+	}
+
+	is_dtmf = is_media_type(rdata, "dtmf");
 
 	if (!is_dtmf && !is_media_type(rdata, "dtmf-relay")) {
 		return 0;
diff --git a/res/res_pjsip_endpoint_identifier_anonymous.c b/res/res_pjsip_endpoint_identifier_anonymous.c
index a7956b5..274c055 100644
--- a/res/res_pjsip_endpoint_identifier_anonymous.c
+++ b/res/res_pjsip_endpoint_identifier_anonymous.c
@@ -110,7 +110,7 @@ static int load_module(void)
 {
 	CHECK_PJSIP_MODULE_LOADED();
 
-	ast_sip_register_endpoint_identifier(&anonymous_identifier);
+	ast_sip_register_endpoint_identifier_with_name(&anonymous_identifier, "anonymous");
 	return AST_MODULE_LOAD_SUCCESS;
 }
 
diff --git a/res/res_pjsip_endpoint_identifier_ip.c b/res/res_pjsip_endpoint_identifier_ip.c
index 4bd4f12..11559ad 100644
--- a/res/res_pjsip_endpoint_identifier_ip.c
+++ b/res/res_pjsip_endpoint_identifier_ip.c
@@ -298,13 +298,6 @@ struct ast_sip_endpoint_formatter endpoint_identify_formatter = {
 	.format_ami = format_ami_endpoint_identify
 };
 
-static int cli_populate_container(void *obj, void *arg, int flags)
-{
-	ao2_link(arg, obj);
-
-	return 0;
-}
-
 static int cli_iterator(void *container, ao2_callback_fn callback, void *args)
 {
 	const struct ast_sip_endpoint *endpoint = container;
@@ -328,47 +321,28 @@ static int cli_iterator(void *container, ao2_callback_fn callback, void *args)
 	return 0;
 }
 
-static int cli_endpoint_gather_identifies(void *obj, void *arg, int flags)
-{
-	struct ast_sip_endpoint *endpoint = obj;
-	struct ao2_container *container = arg;
-
-	cli_iterator(endpoint, cli_populate_container, container);
-
-	return 0;
-}
-
-static struct ao2_container *cli_get_container(void)
+static struct ao2_container *cli_get_container(const char *regex)
 {
-	RAII_VAR(struct ao2_container *, parent_container, NULL, ao2_cleanup);
-	RAII_VAR(struct ao2_container *, s_parent_container, NULL, ao2_cleanup);
-	struct ao2_container *child_container;
+	RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup);
+	struct ao2_container *s_container;
 
-	parent_container =  ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "endpoint",
-		AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
-	if (!parent_container) {
+	container =  ast_sorcery_retrieve_by_regex(ast_sip_get_sorcery(), "identify", regex);
+	if (!container) {
 		return NULL;
 	}
 
-	s_parent_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0,
+	s_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0,
 		ast_sorcery_object_id_sort, ast_sorcery_object_id_compare);
-	if (!s_parent_container) {
+	if (!s_container) {
 		return NULL;
 	}
 
-	if (ao2_container_dup(s_parent_container, parent_container, 0)) {
+	if (ao2_container_dup(s_container, container, 0)) {
+		ao2_ref(s_container, -1);
 		return NULL;
 	}
 
-	child_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0,
-		ast_sorcery_object_id_sort, ast_sorcery_object_id_compare);
-	if (!child_container) {
-		return NULL;
-	}
-
-	ao2_callback(s_parent_container, OBJ_NODATA, cli_endpoint_gather_identifies, child_container);
-
-	return child_container;
+	return s_container;
 }
 
 static void *cli_retrieve_by_id(const char *id)
@@ -461,12 +435,14 @@ static char *my_cli_traverse_objects(struct ast_cli_entry *e, int cmd,
 static struct ast_cli_entry cli_identify[] = {
 AST_CLI_DEFINE(my_cli_traverse_objects, "List PJSIP Identifies",
 	.command = "pjsip list identifies",
-	.usage = "Usage: pjsip list identifies\n"
-	"       List the configured PJSIP Identifies\n"),
+	.usage = "Usage: pjsip list identifies [ like <pattern> ]\n"
+	"       List the configured PJSIP Identifies\n"
+	"       Optional regular expression pattern is used to filter the list.\n"),
 AST_CLI_DEFINE(my_cli_traverse_objects, "Show PJSIP Identifies",
 	.command = "pjsip show identifies",
-	.usage = "Usage: pjsip show identifies\n"
-	"       Show the configured PJSIP Identifies\n"),
+	.usage = "Usage: pjsip show identifies [ like <pattern> ]\n"
+	"       Show the configured PJSIP Identifies\n"
+	"       Optional regular expression pattern is used to filter the list.\n"),
 AST_CLI_DEFINE(my_cli_traverse_objects, "Show PJSIP Identify",
 	.command = "pjsip show identify",
 	.usage = "Usage: pjsip show identify <id>\n"
@@ -489,9 +465,9 @@ static int load_module(void)
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "type", "", OPT_NOOP_T, 0, 0);
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "endpoint", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ip_identify_match, endpoint_name));
 	ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "identify", "match", "", ip_identify_match_handler, match_to_str, match_to_var_list, 0, 0);
-	ast_sorcery_reload_object(ast_sip_get_sorcery(), "identify");
+	ast_sorcery_load_object(ast_sip_get_sorcery(), "identify");
 
-	ast_sip_register_endpoint_identifier(&ip_identifier);
+	ast_sip_register_endpoint_identifier_with_name(&ip_identifier, "ip");
 	ast_sip_register_endpoint_formatter(&endpoint_identify_formatter);
 
 	cli_formatter = ao2_alloc(sizeof(struct ast_sip_cli_formatter_entry), NULL);
diff --git a/res/res_pjsip_endpoint_identifier_user.c b/res/res_pjsip_endpoint_identifier_user.c
index beae1cd..5abf879 100644
--- a/res/res_pjsip_endpoint_identifier_user.c
+++ b/res/res_pjsip_endpoint_identifier_user.c
@@ -116,7 +116,7 @@ static int load_module(void)
 {
 	CHECK_PJSIP_MODULE_LOADED();
 
-	ast_sip_register_endpoint_identifier(&username_identifier);
+	ast_sip_register_endpoint_identifier_with_name(&username_identifier, "username");
 	return AST_MODULE_LOAD_SUCCESS;
 }
 
diff --git a/res/res_pjsip_exten_state.c b/res/res_pjsip_exten_state.c
index 332f45b..4e225dd 100644
--- a/res/res_pjsip_exten_state.c
+++ b/res/res_pjsip_exten_state.c
@@ -37,6 +37,7 @@
 #include "asterisk/astobj2.h"
 #include "asterisk/sorcery.h"
 #include "asterisk/app.h"
+#include "asterisk/taskprocessor.h"
 
 #define BODY_SIZE 1024
 #define EVENT_TYPE_SIZE 50
@@ -53,6 +54,8 @@ struct exten_state_subscription {
 	int id;
 	/*! The SIP subscription */
 	struct ast_sip_subscription *sip_sub;
+	/*! The serializer to use for notifications */
+	struct ast_taskprocessor *serializer;
 	/*! Context in which subscription looks for updates */
 	char context[AST_MAX_CONTEXT];
 	/*! Extension within the context to receive updates from */
@@ -112,7 +115,8 @@ static void exten_state_subscription_destructor(void *obj)
 	struct exten_state_subscription *sub = obj;
 
 	ast_free(sub->user_agent);
-	ao2_cleanup(sub->sip_sub);
+	ast_sip_subscription_destroy(sub->sip_sub);
+	ast_taskprocessor_unreference(sub->serializer);
 }
 
 static char *get_user_agent(const struct ast_sip_subscription *sip_sub)
@@ -156,7 +160,14 @@ static struct exten_state_subscription *exten_state_subscription_alloc(
 		return NULL;
 	}
 
-	exten_state_sub->sip_sub = ao2_bump(sip_sub);
+	exten_state_sub->sip_sub = sip_sub;
+
+	/* We keep our own reference to the serializer as there is no guarantee in state_changed
+	 * that the subscription tree is still valid when it is called. This can occur when
+	 * the subscription is terminated at around the same time as the state_changed
+	 * callback is invoked.
+	 */
+	exten_state_sub->serializer = ao2_bump(ast_sip_subscription_get_serializer(sip_sub));
 	exten_state_sub->last_exten_state = INITIAL_LAST_EXTEN_STATE;
 	exten_state_sub->last_presence_state = AST_PRESENCE_NOT_SET;
 	exten_state_sub->user_agent = get_user_agent(sip_sub);
@@ -205,14 +216,9 @@ static struct notify_task_data *alloc_notify_task_data(char *exten, struct exten
 	task_data->exten_state_data.device_state_info = ao2_bump(info->device_state_info);
 	task_data->exten_state_data.sub = exten_state_sub->sip_sub;
 
-	ast_sip_subscription_get_local_uri(exten_state_sub->sip_sub,
-			task_data->exten_state_data.local, sizeof(task_data->exten_state_data.local));
-	ast_sip_subscription_get_remote_uri(exten_state_sub->sip_sub,
-			task_data->exten_state_data.remote, sizeof(task_data->exten_state_data.remote));
-
 	if ((info->exten_state == AST_EXTENSION_DEACTIVATED) ||
 	    (info->exten_state == AST_EXTENSION_REMOVED)) {
-		ast_log(LOG_WARNING, "Watcher for hint %s %s\n", exten, info->exten_state
+		ast_verb(2, "Watcher for hint %s %s\n", exten, info->exten_state
 			 == AST_EXTENSION_REMOVED ? "removed" : "deactivated");
 		task_data->terminate = 1;
 	}
@@ -228,6 +234,19 @@ static int notify_task(void *obj)
 		.body_data = &task_data->exten_state_data,
 	};
 
+	/* Terminated subscriptions are no longer associated with a valid tree, and sending
+	 * NOTIFY messages on a subscription which has already been terminated won't work.
+	 */
+	if (ast_sip_subscription_is_terminated(task_data->exten_state_sub->sip_sub)) {
+		return 0;
+	}
+
+	/* All access to the subscription must occur within a task executed within its serializer */
+	ast_sip_subscription_get_local_uri(task_data->exten_state_sub->sip_sub,
+			task_data->exten_state_data.local, sizeof(task_data->exten_state_data.local));
+	ast_sip_subscription_get_remote_uri(task_data->exten_state_sub->sip_sub,
+			task_data->exten_state_data.remote, sizeof(task_data->exten_state_data.remote));
+
 	/* Pool allocation has to happen here so that we allocate within a PJLIB thread */
 	task_data->exten_state_data.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(),
 			"exten_state", 1024, 1024);
@@ -263,8 +282,8 @@ static int state_changed(char *context, char *exten,
 
 	/* safe to push this async since we copy the data from info and
 	   add a ref for the device state info */
-	if (ast_sip_push_task(ast_sip_subscription_get_serializer(task_data->exten_state_sub->sip_sub),
-			      notify_task, task_data)) {
+	if (ast_sip_push_task(task_data->exten_state_sub->serializer, notify_task,
+		task_data)) {
 		ao2_cleanup(task_data);
 		return -1;
 	}
@@ -334,7 +353,8 @@ static int new_subscribe(struct ast_sip_endpoint *endpoint,
 		const char *resource)
 {
 	if (!ast_exists_extension(NULL, endpoint->context, resource, PRIORITY_HINT, NULL)) {
-		ast_log(LOG_WARNING, "Extension %s does not exist or has no associated hint\n", resource);
+		ast_log(LOG_NOTICE, "Extension state subscription failed: Extension %s does not exist in context '%s' or has no associated hint\n",
+			resource, endpoint->context);
 		return 404;
 	}
 
@@ -400,6 +420,7 @@ static struct ast_sip_exten_state_data *exten_state_data_alloc(struct ast_sip_su
 	struct ast_sip_exten_state_data *exten_state_data;
 	char *subtype = NULL;
 	char *message = NULL;
+	int presence_state;
 
 	exten_state_data = ao2_alloc(sizeof(*exten_state_data), exten_state_data_destructor);
 	if (!exten_state_data) {
@@ -407,11 +428,12 @@ static struct ast_sip_exten_state_data *exten_state_data_alloc(struct ast_sip_su
 	}
 
 	exten_state_data->exten = exten_state_sub->exten;
-	if ((exten_state_data->presence_state = ast_hint_presence_state(NULL, exten_state_sub->context,
-			exten_state_sub->exten, &subtype, &message)) == -1) {
+	presence_state = ast_hint_presence_state(NULL, exten_state_sub->context, exten_state_sub->exten, &subtype, &message);
+	if (presence_state  == -1 || presence_state == AST_PRESENCE_INVALID) {
 		ao2_cleanup(exten_state_data);
 		return NULL;
 	}
+	exten_state_data->presence_state = presence_state;
 	exten_state_data->presence_subtype = subtype;
 	exten_state_data->presence_message = message;
 	exten_state_data->user_agent = exten_state_sub->user_agent;
diff --git a/res/res_pjsip_keepalive.c b/res/res_pjsip_keepalive.c
new file mode 100644
index 0000000..b854fc9
--- /dev/null
+++ b/res/res_pjsip_keepalive.c
@@ -0,0 +1,269 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2014, Digium, Inc.
+ *
+ * Joshua Colp <jcolp 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.
+ */
+
+/*** MODULEINFO
+	<depend>pjproject</depend>
+	<depend>res_pjsip</depend>
+	<support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+#include <pjsip.h>
+#include <pjsip_ua.h>
+
+#include "asterisk/res_pjsip.h"
+#include "asterisk/module.h"
+#include "asterisk/astobj2.h"
+
+/*! \brief Number of buckets for keepalive transports */
+#define KEEPALIVE_TRANSPORTS_BUCKETS 53
+
+/*! \brief The keep alive packet to send */
+static const pj_str_t keepalive_packet = { "\r\n\r\n", 4 };
+
+/*! \brief Global container of active transports */
+static struct ao2_container *transports;
+
+/*! \brief Thread keeping things alive */
+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 kept alive */
+struct keepalive_transport {
+	/*! \brief The underlying PJSIP transport */
+	pjsip_transport *transport;
+};
+
+/*! \brief Callback function to send keepalive */
+static int keepalive_transport_cb(void *obj, void *arg, int flags)
+{
+	struct keepalive_transport *keepalive = obj;
+	pjsip_tpselector selector = {
+		.type = PJSIP_TPSELECTOR_TRANSPORT,
+		.u.transport = keepalive->transport,
+	};
+
+	pjsip_tpmgr_send_raw(pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()),
+		keepalive->transport->key.type, &selector, NULL, keepalive_packet.ptr, keepalive_packet.slen,
+		&keepalive->transport->key.rem_addr, pj_sockaddr_get_len(&keepalive->transport->key.rem_addr),
+		NULL, NULL);
+
+	return 0;
+}
+
+/*! \brief Thread which sends keepalives to all active connection-oriented transports */
+static void *keepalive_transport_thread(void *data)
+{
+	pj_thread_desc desc;
+	pj_thread_t *thread;
+
+	if (pj_thread_register("Asterisk Keepalive Thread", desc, &thread) != PJ_SUCCESS) {
+		ast_log(LOG_ERROR, "Could not register keepalive thread with PJLIB, keepalives will not occur.\n");
+		return NULL;
+	}
+
+	/* Once loaded this module just keeps on going as it is unsafe to stop and change the underlying
+	 * callback for the transport manager.
+	 */
+	while (1) {
+		sleep(keepalive_interval);
+		ao2_callback(transports, OBJ_NODATA, keepalive_transport_cb, NULL);
+	}
+
+	return NULL;
+}
+
+/*! \brief Destructor for keepalive transport */
+static void keepalive_transport_destroy(void *obj)
+{
+	struct keepalive_transport *keepalive = obj;
+
+	pjsip_transport_dec_ref(keepalive->transport);
+}
+
+/*! \brief Callback invoked when transport changes occur */
+static void keepalive_transport_state_callback(pjsip_transport *transport, pjsip_transport_state state,
+	const pjsip_transport_state_info *info)
+{
+	/* We only care about connection-oriented transports */
+	if (transport->flag & PJSIP_TRANSPORT_RELIABLE) {
+		struct keepalive_transport *keepalive;
+
+		switch (state) {
+		case PJSIP_TP_STATE_CONNECTED:
+			keepalive = ao2_alloc(sizeof(*keepalive), keepalive_transport_destroy);
+			if (keepalive) {
+				keepalive->transport = transport;
+				pjsip_transport_add_ref(keepalive->transport);
+				ao2_link(transports, keepalive);
+				ao2_ref(keepalive, -1);
+			}
+			break;
+		case PJSIP_TP_STATE_DISCONNECTED:
+			ao2_find(transports, transport->obj_name, OBJ_SEARCH_KEY | OBJ_NODATA | OBJ_UNLINK);
+			break;
+		default:
+			break;
+		}
+	}
+
+	/* Forward to the old state callback if present */
+	if (tpmgr_state_callback) {
+		tpmgr_state_callback(transport, state, info);
+	}
+}
+
+/*! \brief Hashing function for keepalive transport */
+static int keepalive_transport_hash_fn(const void *obj, int flags)
+{
+	const struct keepalive_transport *object;
+	const char *key;
+
+	switch (flags & OBJ_SEARCH_MASK) {
+	case OBJ_SEARCH_KEY:
+		key = obj;
+		break;
+	case OBJ_SEARCH_OBJECT:
+		object = obj;
+		key = object->transport->obj_name;
+		break;
+	default:
+		/* Hash can only work on something with a full key. */
+		ast_assert(0);
+		return 0;
+	}
+	return ast_str_hash(key);
+}
+
+/*! \brief Comparison function for keepalive transport */
+static int keepalive_transport_cmp_fn(void *obj, void *arg, int flags)
+{
+	const struct keepalive_transport *object_left = obj;
+	const struct keepalive_transport *object_right = arg;
+	const char *right_key = arg;
+	int cmp;
+
+	switch (flags & OBJ_SEARCH_MASK) {
+	case OBJ_SEARCH_OBJECT:
+		right_key = object_right->transport->obj_name;
+		/* Fall through */
+	case OBJ_SEARCH_KEY:
+		cmp = strcmp(object_left->transport->obj_name, right_key);
+		break;
+	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.
+		 */
+		cmp = strncmp(object_left->transport->obj_name, right_key, strlen(right_key));
+		break;
+	default:
+		/*
+		 * What arg points to is specific to this traversal callback
+		 * and has no special meaning to astobj2.
+		 */
+		cmp = 0;
+		break;
+	}
+
+	return !cmp ? CMP_MATCH | CMP_STOP : 0;
+}
+
+static void keepalive_global_loaded(const char *object_type)
+{
+	unsigned int new_interval = ast_sip_get_keep_alive_interval();
+	pjsip_tpmgr *tpmgr;
+
+	if (new_interval) {
+		keepalive_interval = new_interval;
+	} else if (keepalive_interval) {
+		ast_log(LOG_NOTICE, "Keepalive support can not be disabled once activated.\n");
+		return;
+	} else {
+		/* This will occur if no keepalive interval has been specified at initial start */
+		return;
+	}
+
+	if (keepalive_thread != AST_PTHREADT_NULL) {
+		return;
+	}
+
+	transports = ao2_container_alloc(KEEPALIVE_TRANSPORTS_BUCKETS, keepalive_transport_hash_fn,
+		keepalive_transport_cmp_fn);
+	if (!transports) {
+		ast_log(LOG_ERROR, "Could not create container for transports to perform keepalive on.\n");
+		return;
+	}
+
+	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");
+		ao2_ref(transports, -1);
+		return;
+	}
+
+	if (ast_pthread_create(&keepalive_thread, NULL, keepalive_transport_thread, NULL)) {
+		ast_log(LOG_ERROR, "Could not create thread for sending keepalive messages.\n");
+		ao2_ref(transports, -1);
+		return;
+	}
+
+	tpmgr_state_callback = pjsip_tpmgr_get_state_cb(tpmgr);
+	pjsip_tpmgr_set_state_cb(tpmgr, &keepalive_transport_state_callback);
+}
+
+/*! \brief Observer which is used to update our interval when the global setting changes */
+static struct ast_sorcery_observer keepalive_global_observer = {
+	.loaded = keepalive_global_loaded,
+};
+
+static int load_module(void)
+{
+	CHECK_PJSIP_MODULE_LOADED();
+
+	ast_sorcery_observer_add(ast_sip_get_sorcery(), "global", &keepalive_global_observer);
+	ast_sorcery_reload_object(ast_sip_get_sorcery(), "global");
+	ast_module_shutdown_ref(ast_module_info->self);
+	return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+	/* This will never get called */
+	return 0;
+}
+
+static int reload_module(void)
+{
+	ast_sorcery_reload_object(ast_sip_get_sorcery(), "global");
+	return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Stateful Connection Keepalive Support",
+		.support_level = AST_MODULE_SUPPORT_CORE,
+		.load = load_module,
+		.reload = reload_module,
+		.unload = unload_module,
+		.load_pri = AST_MODPRI_CHANNEL_DEPEND - 4,
+		   );
diff --git a/res/res_pjsip_log_forwarder.c b/res/res_pjsip_log_forwarder.c
index 64e47b4..7b095bb 100644
--- a/res/res_pjsip_log_forwarder.c
+++ b/res/res_pjsip_log_forwarder.c
@@ -39,7 +39,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <pjsip.h>
 #include <pj/log.h>
diff --git a/res/res_pjsip_logger.c b/res/res_pjsip_logger.c
index dab9832..2e5c76c 100644
--- a/res/res_pjsip_logger.c
+++ b/res/res_pjsip_logger.c
@@ -25,7 +25,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 425691 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <pjsip.h>
 
diff --git a/res/res_pjsip_messaging.c b/res/res_pjsip_messaging.c
index 0433299..dab70ca 100644
--- a/res/res_pjsip_messaging.c
+++ b/res/res_pjsip_messaging.c
@@ -42,6 +42,7 @@
 #include "asterisk/pbx.h"
 #include "asterisk/res_pjsip.h"
 #include "asterisk/res_pjsip_session.h"
+#include "asterisk/taskprocessor.h"
 
 const pjsip_method pjsip_message_method = {PJSIP_OTHER_METHOD, {"MESSAGE", 7} };
 
@@ -49,6 +50,8 @@ const pjsip_method pjsip_message_method = {PJSIP_OTHER_METHOD, {"MESSAGE", 7} };
 #define MAX_BODY_SIZE 1024
 #define MAX_USER_SIZE 128
 
+static struct ast_taskprocessor *message_serializer;
+
 /*!
  * \internal
  * \brief Checks to make sure the request has the correct content type.
@@ -427,13 +430,13 @@ static char *sip_to_pjsip(char *buf, int size, int capacity)
  */
 static enum pjsip_status_code rx_data_to_ast_msg(pjsip_rx_data *rdata, struct ast_msg *msg)
 {
-	struct ast_sip_endpoint *endpt = ast_pjsip_rdata_get_endpoint(rdata);
+	RAII_VAR(struct ast_sip_endpoint *, endpt, NULL, ao2_cleanup);
 	pjsip_uri *ruri = rdata->msg_info.msg->line.req.uri;
 	pjsip_sip_uri *sip_ruri;
 	pjsip_name_addr *name_addr;
 	char buf[MAX_BODY_SIZE];
 	const char *field;
-	const char *context = S_OR(endpt->message_context, endpt->context);
+	const char *context;
 	char exten[AST_MAX_EXTENSION];
 	int res = 0;
 	int size;
@@ -445,6 +448,10 @@ static enum pjsip_status_code rx_data_to_ast_msg(pjsip_rx_data *rdata, struct as
 	sip_ruri = pjsip_uri_get_uri(ruri);
 	ast_copy_pj_str(exten, &sip_ruri->user, AST_MAX_EXTENSION);
 
+	endpt = ast_pjsip_rdata_get_endpoint(rdata);
+	ast_assert(endpt != NULL);
+
+	context = S_OR(endpt->message_context, endpt->context);
 	res |= ast_msg_set_context(msg, "%s", context);
 	res |= ast_msg_set_exten(msg, "%s", exten);
 
@@ -589,7 +596,7 @@ static int sip_msg_send(const struct ast_msg *msg, const char *to, const char *f
 	}
 
 	if (!(mdata = msg_data_create(msg, to, from)) ||
-	    ast_sip_push_task(NULL, msg_send, mdata)) {
+	    ast_sip_push_task(message_serializer, msg_send, mdata)) {
 		ao2_ref(mdata, -1);
 		return -1;
 	}
@@ -606,7 +613,6 @@ static pj_status_t send_response(pjsip_rx_data *rdata, enum pjsip_status_code co
 {
 	pjsip_tx_data *tdata;
 	pj_status_t status;
-	pjsip_response_addr res_addr;
 
 	status = ast_sip_create_response(rdata, code, NULL, &tdata);
 	if (status != PJ_SUCCESS) {
@@ -617,13 +623,11 @@ static pj_status_t send_response(pjsip_rx_data *rdata, enum pjsip_status_code co
 	if (dlg && tsx) {
 		status = pjsip_dlg_send_response(dlg, tsx, tdata);
 	} else {
-		/* Get where to send request. */
-		status = pjsip_get_response_addr(tdata->pool, rdata, &res_addr);
-		if (status != PJ_SUCCESS) {
-			ast_log(LOG_ERROR, "Unable to get response address (%d)\n", status);
-			return status;
-		}
-		status = ast_sip_send_response(&res_addr, tdata, ast_pjsip_rdata_get_endpoint(rdata));
+		struct ast_sip_endpoint *endpoint;
+
+		endpoint = ast_pjsip_rdata_get_endpoint(rdata);
+		status = ast_sip_send_stateful_response(rdata, tdata, endpoint);
+		ao2_cleanup(endpoint);
 	}
 
 	if (status != PJ_SUCCESS) {
@@ -669,9 +673,16 @@ static pj_bool_t module_on_rx_request(pjsip_rx_data *rdata)
 		return PJ_TRUE;
 	}
 
-	/* send it to the messaging core */
-	ast_msg_queue(msg);
-	send_response(rdata, PJSIP_SC_ACCEPTED, NULL, NULL);
+	/* Send it to the messaging core.
+	 *
+	 * If we are unable to send a response, the most likely reason is that we
+	 * are handling a retransmission of an incoming MESSAGE and were unable to
+	 * create a transaction due to a duplicate key. If we are unable to send
+	 * a response, we should not queue the message to the dialplan
+	 */
+	if (!send_response(rdata, PJSIP_SC_ACCEPTED, NULL, NULL)) {
+		ast_msg_queue(msg);
+	}
 
 	return PJ_TRUE;
 }
@@ -681,10 +692,14 @@ static int incoming_in_dialog_request(struct ast_sip_session *session, struct pj
 	char buf[MAX_BODY_SIZE];
 	enum pjsip_status_code code;
 	struct ast_frame f;
-
 	pjsip_dialog *dlg = session->inv_session->dlg;
 	pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
 
+	if (!session->channel) {
+		send_response(rdata, PJSIP_SC_NOT_FOUND, dlg, tsx);
+		return 0;
+	}
+
 	if ((code = check_content_type(rdata)) != PJSIP_SC_OK) {
 		send_response(rdata, code, dlg, tsx);
 		return 0;
@@ -692,6 +707,7 @@ static int incoming_in_dialog_request(struct ast_sip_session *session, struct pj
 
 	if (print_body(rdata, buf, sizeof(buf)-1) < 1) {
 		/* invalid body size */
+		send_response(rdata, PJSIP_SC_REQUEST_ENTITY_TOO_LARGE, dlg, tsx);
 		return 0;
 	}
 
@@ -742,6 +758,13 @@ static int load_module(void)
 		return AST_MODULE_LOAD_DECLINE;
 	}
 
+	message_serializer = ast_sip_create_serializer();
+	if (!message_serializer) {
+		ast_sip_unregister_service(&messaging_module);
+		ast_msg_tech_unregister(&msg_tech);
+		return AST_MODULE_LOAD_DECLINE;
+	}
+
 	ast_sip_session_register_supplement(&messaging_supplement);
 	return AST_MODULE_LOAD_SUCCESS;
 }
@@ -751,6 +774,7 @@ static int unload_module(void)
 	ast_sip_session_unregister_supplement(&messaging_supplement);
 	ast_msg_tech_unregister(&msg_tech);
 	ast_sip_unregister_service(&messaging_module);
+	ast_taskprocessor_unreference(message_serializer);
 	return 0;
 }
 
diff --git a/res/res_pjsip_multihomed.c b/res/res_pjsip_multihomed.c
index e0ee53e..437a1cb 100644
--- a/res/res_pjsip_multihomed.c
+++ b/res/res_pjsip_multihomed.c
@@ -30,12 +30,6 @@
 #include "asterisk/res_pjsip.h"
 #include "asterisk/module.h"
 
-/*! \brief Local host address for IPv4 */
-static char host_ipv4[PJ_INET_ADDRSTRLEN + 2];
-
-/*! \brief Local host address for IPv6 */
-static char host_ipv6[PJ_INET6_ADDRSTRLEN + 2];
-
 /*! \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)
 {
@@ -75,20 +69,21 @@ static int multihomed_rewrite_sdp(struct pjmedia_sdp_session *sdp)
 	}
 
 	/* If the host address is used in the SDP replace it with the address of what this is going out on */
-	if ((!pj_strcmp2(&sdp->conn->addr_type, "IP4") && !pj_strcmp2(&sdp->conn->addr, host_ipv4)) ||
-		(!pj_strcmp2(&sdp->conn->addr_type, "IP6") && !pj_strcmp2(&sdp->conn->addr, host_ipv6))) {
+	if ((!pj_strcmp2(&sdp->conn->addr_type, "IP4") && !pj_strcmp2(&sdp->conn->addr,
+		ast_sip_get_host_ip_string(pj_AF_INET()))) ||
+		(!pj_strcmp2(&sdp->conn->addr_type, "IP6") && !pj_strcmp2(&sdp->conn->addr,
+		ast_sip_get_host_ip_string(pj_AF_INET6())))) {
 		return 1;
 	}
 
 	return 0;
 }
 
-/*! \brief Helper function which determines if the existing address has priority over new one */
-static int multihomed_rewrite_header(pj_str_t *source, pjsip_transport *transport)
+/*! \brief Helper function which determines if a transport is bound to any */
+static int multihomed_bound_any(pjsip_transport *transport)
 {
 	pj_uint32_t loop6[4] = {0, 0, 0, 0};
 
-	/* If the transport is bound to any it should always rewrite */
 	if ((transport->local_addr.addr.sa_family == pj_AF_INET() &&
 		transport->local_addr.ipv4.sin_addr.s_addr == PJ_INADDR_ANY) ||
 		(transport->local_addr.addr.sa_family == pj_AF_INET6() &&
@@ -96,18 +91,12 @@ static int multihomed_rewrite_header(pj_str_t *source, pjsip_transport *transpor
 		return 1;
 	}
 
-	/* If the transport is explicitly bound but the determined source differs favor the transport */
-	if (!pj_strcmp(source, &transport->local_name.host)) {
-		return 1;
-	}
-
 	return 0;
 }
 
 static pj_status_t multihomed_on_tx_message(pjsip_tx_data *tdata)
 {
 	pjsip_tpmgr_fla2_param prm;
-	pjsip_transport *transport = NULL;
 	pjsip_cseq_hdr *cseq;
 	pjsip_via_hdr *via;
 
@@ -122,35 +111,46 @@ static pj_status_t multihomed_on_tx_message(pjsip_tx_data *tdata)
 		return PJ_SUCCESS;
 	}
 
-	/* If the transport it is going out on is different reflect it in the message */
-	if (tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_UDP ||
-		tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_UDP6) {
-		transport = multihomed_get_udp_transport(&prm.ret_addr, prm.ret_port);
-	}
+	/* The port in the message should always be that of the original transport */
+	prm.ret_port = tdata->tp_info.transport->local_name.port;
 
-	/* If no new transport use the one provided by the message */
-	if (!transport) {
-		transport = tdata->tp_info.transport;
-	}
+	/* If the IP source differs from the existing transport see if we need to update it */
+	if (pj_strcmp(&prm.ret_addr, &tdata->tp_info.transport->local_name.host)) {
 
-	/* If the message should not be rewritten then abort early */
-	if (!multihomed_rewrite_header(&prm.ret_addr, transport)) {
-		return PJ_SUCCESS;
-	}
+		/* If the transport it is going out on is different reflect it in the message */
+		if (tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_UDP ||
+			tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_UDP6) {
+			pjsip_transport *transport;
 
-	/* Update the transport in case it has changed - we do this now in case we don't want to touch the message above */
-	tdata->tp_info.transport = transport;
+			transport = multihomed_get_udp_transport(&prm.ret_addr, prm.ret_port);
+
+			if (transport) {
+				tdata->tp_info.transport = transport;
+			}
+		}
+
+		/* 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)) {
+			pj_strassign(&prm.ret_addr, &tdata->tp_info.transport->local_name.host);
+		}
+	} else {
+		/* The transport chosen will deliver this but ensure it is updated with the right information */
+		pj_strassign(&prm.ret_addr, &tdata->tp_info.transport->local_name.host);
+	}
 
 	/* If the message needs to be updated with new address do so */
 	if (tdata->msg->type == PJSIP_REQUEST_MSG || !(cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL)) ||
 		pj_strcmp2(&cseq->method.name, "REGISTER")) {
 		pjsip_contact_hdr *contact = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, NULL);
-		if (contact && (PJSIP_URI_SCHEME_IS_SIP(contact->uri) || PJSIP_URI_SCHEME_IS_SIPS(contact->uri))) {
+		if (contact && (PJSIP_URI_SCHEME_IS_SIP(contact->uri) || PJSIP_URI_SCHEME_IS_SIPS(contact->uri))
+			&& !(tdata->msg->type == PJSIP_RESPONSE_MSG && tdata->msg->line.status.code / 100 == 3)) {
 			pjsip_sip_uri *uri = pjsip_uri_get_uri(contact->uri);
 
-			/* prm.ret_addr is allocated from the tdata pool so it is perfectly fine to just do an assignment like this */
+			/* 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",
+				(int)pj_strlen(&uri->host), pj_strbuf(&uri->host), uri->port);
 
 			pjsip_tx_data_invalidate_msg(tdata);
 		}
@@ -200,7 +200,6 @@ static int unload_module(void)
 static int load_module(void)
 {
 	char hostname[MAXHOSTNAMELEN] = "";
-	pj_sockaddr addr;
 
 	CHECK_PJSIP_MODULE_LOADED();
 
@@ -209,16 +208,6 @@ static int load_module(void)
 			hostname);
 	}
 
-	if (!pj_gethostip(pj_AF_INET(), &addr)) {
-		pj_sockaddr_print(&addr, host_ipv4, sizeof(host_ipv4), 2);
-		ast_verb(3, "Local IPv4 address determined to be: %s\n", host_ipv4);
-	}
-
-	if (!pj_gethostip(pj_AF_INET6(), &addr)) {
-		pj_sockaddr_print(&addr, host_ipv6, sizeof(host_ipv6), 2);
-		ast_verb(3, "Local IPv6 address determined to be: %s\n", host_ipv6);
-	}
-
 	if (ast_sip_register_service(&multihomed_module)) {
 		ast_log(LOG_ERROR, "Could not register multihomed module for incoming and outgoing requests\n");
 		return AST_MODULE_LOAD_FAILURE;
diff --git a/res/res_pjsip_mwi.c b/res/res_pjsip_mwi.c
index bf0925d..f349324 100644
--- a/res/res_pjsip_mwi.c
+++ b/res/res_pjsip_mwi.c
@@ -40,7 +40,7 @@
 #include "asterisk/app.h"
 
 struct mwi_subscription;
-AO2_GLOBAL_OBJ_STATIC(unsolicited_mwi);
+static struct ao2_container *unsolicited_mwi;
 
 #define STASIS_BUCKETS 13
 #define MWI_BUCKETS 53
@@ -100,12 +100,14 @@ struct mwi_stasis_subscription {
  */
 struct mwi_subscription {
 	/*! Container of \ref mwi_stasis_subscription structures.
-	 * A single MWI subscription may be fore multiple mailboxes, thus
+	 * A single MWI subscription may be for multiple mailboxes, thus
 	 * requiring multiple stasis subscriptions
 	 */
 	struct ao2_container *stasis_subs;
 	/*! The SIP subscription. Unsolicited MWI does not use this */
 	struct ast_sip_subscription *sip_sub;
+	/*! AORs we should react to for unsolicited MWI NOTIFY */
+	char *aors;
 	/*! Is the MWI solicited (i.e. Initiated with an external SUBSCRIBE) ? */
 	unsigned int is_solicited;
 	/*! Identifier for the subscription.
@@ -136,9 +138,17 @@ static struct mwi_stasis_subscription *mwi_stasis_subscription_alloc(const char
 
 	/* Safe strcpy */
 	strcpy(mwi_stasis_sub->mailbox, mailbox);
+
+	ast_debug(3, "Creating stasis MWI subscription to mailbox %s for endpoint %s\n",
+		mailbox, mwi_sub->id);
 	ao2_ref(mwi_sub, +1);
-	ast_debug(3, "Creating stasis MWI subscription to mailbox %s for endpoint %s\n", mailbox, mwi_sub->id);
 	mwi_stasis_sub->stasis_sub = stasis_subscribe_pool(topic, mwi_stasis_cb, mwi_sub);
+	if (!mwi_stasis_sub->stasis_sub) {
+		/* Failed to subscribe. */
+		ao2_ref(mwi_stasis_sub, -1);
+		ao2_ref(mwi_sub, -1);
+		mwi_stasis_sub = NULL;
+	}
 	return mwi_stasis_sub;
 }
 
@@ -194,8 +204,11 @@ static void mwi_subscription_destructor(void *obj)
 	struct mwi_subscription *sub = obj;
 
 	ast_debug(3, "Destroying MWI subscription for endpoint %s\n", sub->id);
-	ao2_cleanup(sub->sip_sub);
+	if (sub->is_solicited) {
+		ast_sip_subscription_destroy(sub->sip_sub);
+	}
 	ao2_cleanup(sub->stasis_subs);
+	ast_free(sub->aors);
 }
 
 static struct mwi_subscription *mwi_subscription_alloc(struct ast_sip_endpoint *endpoint,
@@ -222,7 +235,7 @@ static struct mwi_subscription *mwi_subscription_alloc(struct ast_sip_endpoint *
 	 * state not being updated on the device
 	 */
 	if (is_solicited) {
-		sub->sip_sub = ao2_bump(sip_sub);
+		sub->sip_sub = sip_sub;
 	}
 
 	sub->stasis_subs = ao2_container_alloc(STASIS_BUCKETS, stasis_sub_hash, stasis_sub_cmp);
@@ -232,6 +245,14 @@ static struct mwi_subscription *mwi_subscription_alloc(struct ast_sip_endpoint *
 	}
 	sub->is_solicited = is_solicited;
 
+	if (!is_solicited && !ast_strlen_zero(endpoint->aors)) {
+		sub->aors = ast_strdup(endpoint->aors);
+		if (!sub->aors) {
+			ao2_ref(sub, -1);
+			return NULL;
+		}
+	}
+
 	ast_debug(3, "Created %s MWI subscription for endpoint %s\n", is_solicited ? "solicited" : "unsolicited", sub->id);
 
 	return sub;
@@ -286,7 +307,7 @@ static int mwi_sub_cmp(void *obj, void *arg, int flags)
 
 static int get_message_count(void *obj, void *arg, int flags)
 {
-	RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
+	struct stasis_message *msg;
 	struct mwi_stasis_subscription *mwi_stasis = obj;
 	struct ast_sip_message_accumulator *counter = arg;
 	struct ast_mwi_state *mwi_state;
@@ -299,6 +320,9 @@ static int get_message_count(void *obj, void *arg, int flags)
 	mwi_state = stasis_message_data(msg);
 	counter->old_msgs += mwi_state->old_msgs;
 	counter->new_msgs += mwi_state->new_msgs;
+
+	ao2_ref(msg, -1);
+
 	return 0;
 }
 
@@ -424,7 +448,7 @@ static void send_unsolicited_mwi_notify(struct mwi_subscription *sub,
 
 		contacts = ast_sip_location_retrieve_aor_contacts(aor);
 		if (!contacts || (ao2_container_count(contacts) == 0)) {
-			ast_log(LOG_WARNING, "No contacts bound to AOR %s. Cannot send unsolicited MWI.\n", aor_name);
+			ast_log(LOG_NOTICE, "No contacts bound to AOR %s. Cannot send unsolicited MWI until a contact registers.\n", aor_name);
 			continue;
 		}
 
@@ -460,7 +484,7 @@ static int unsubscribe_stasis(void *obj, void *arg, int flags)
 	struct mwi_stasis_subscription *mwi_stasis = obj;
 	if (mwi_stasis->stasis_sub) {
 		ast_debug(3, "Removing stasis subscription to mailbox %s\n", mwi_stasis->mailbox);
-		mwi_stasis->stasis_sub = stasis_unsubscribe(mwi_stasis->stasis_sub);
+		mwi_stasis->stasis_sub = stasis_unsubscribe_and_join(mwi_stasis->stasis_sub);
 	}
 	return CMP_MATCH;
 }
@@ -468,31 +492,50 @@ static int unsubscribe_stasis(void *obj, void *arg, int flags)
 static void mwi_subscription_shutdown(struct ast_sip_subscription *sub)
 {
 	struct mwi_subscription *mwi_sub;
-	RAII_VAR(struct ast_datastore *, mwi_datastore,
-			ast_sip_subscription_get_datastore(sub, MWI_DATASTORE), ao2_cleanup);
+	struct ast_datastore *mwi_datastore;
 
+	mwi_datastore = ast_sip_subscription_get_datastore(sub, MWI_DATASTORE);
 	if (!mwi_datastore) {
 		return;
 	}
 
 	mwi_sub = mwi_datastore->data;
 	ao2_callback(mwi_sub->stasis_subs, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, unsubscribe_stasis, NULL);
+	ast_sip_subscription_remove_datastore(sub, MWI_DATASTORE);
+
+	ao2_ref(mwi_datastore, -1);
 }
 
-static struct ast_datastore_info mwi_ds_info = { };
+static void mwi_ds_destroy(void *data)
+{
+	struct mwi_subscription *sub = data;
+
+	ao2_ref(sub, -1);
+}
+
+static struct ast_datastore_info mwi_ds_info = {
+	.destroy = mwi_ds_destroy,
+};
 
 static int add_mwi_datastore(struct mwi_subscription *sub)
 {
-	RAII_VAR(struct ast_datastore *, mwi_datastore, NULL, ao2_cleanup);
+	struct ast_datastore *mwi_datastore;
+	int res;
 
 	mwi_datastore = ast_sip_subscription_alloc_datastore(&mwi_ds_info, MWI_DATASTORE);
 	if (!mwi_datastore) {
 		return -1;
 	}
+	ao2_ref(sub, +1);
 	mwi_datastore->data = sub;
 
-	ast_sip_subscription_add_datastore(sub->sip_sub, mwi_datastore);
-	return 0;
+	/*
+	 * NOTE:  Adding the datastore to the subscription creates a ref loop
+	 * that must be manually broken.
+	 */
+	res = ast_sip_subscription_add_datastore(sub->sip_sub, mwi_datastore);
+	ao2_ref(mwi_datastore, -1);
+	return res;
 }
 
 /*!
@@ -506,18 +549,12 @@ static int add_mwi_datastore(struct mwi_subscription *sub)
 static int endpoint_receives_unsolicited_mwi_for_mailbox(struct ast_sip_endpoint *endpoint,
 		const char *mailbox)
 {
-	struct ao2_container *unsolicited = ao2_global_obj_ref(unsolicited_mwi);
 	struct ao2_iterator *mwi_subs;
 	struct mwi_subscription *mwi_sub;
 	const char *endpoint_id = ast_sorcery_object_get_id(endpoint);
 	int ret = 0;
 
-	if (!unsolicited) {
-		return 0;
-	}
-
-	mwi_subs = ao2_find(unsolicited, endpoint_id, OBJ_SEARCH_KEY | OBJ_MULTIPLE);
-	ao2_cleanup(unsolicited);
+	mwi_subs = ao2_find(unsolicited_mwi, endpoint_id, OBJ_SEARCH_KEY | OBJ_MULTIPLE);
 
 	if (!mwi_subs) {
 		return 0;
@@ -586,11 +623,15 @@ static int mwi_on_aor(void *obj, void *arg, int flags)
 
 	mailboxes = ast_strdupa(aor->mailboxes);
 	while ((mailbox = strsep(&mailboxes, ","))) {
-		RAII_VAR(struct mwi_stasis_subscription *, mwi_stasis_sub,
-				mwi_stasis_subscription_alloc(mailbox, sub), ao2_cleanup);
-		if (mwi_stasis_sub) {
-			ao2_link(sub->stasis_subs, mwi_stasis_sub);
+		struct mwi_stasis_subscription *mwi_stasis_sub;
+
+		mwi_stasis_sub = mwi_stasis_subscription_alloc(mailbox, sub);
+		if (!mwi_stasis_sub) {
+			continue;
 		}
+
+		ao2_link(sub->stasis_subs, mwi_stasis_sub);
+		ao2_ref(mwi_stasis_sub, -1);
 	}
 
 	return 0;
@@ -606,8 +647,8 @@ static struct mwi_subscription *mwi_create_subscription(
 	}
 
 	if (add_mwi_datastore(sub)) {
-		ast_log(LOG_WARNING, "Unable to allocate datastore on MWI "
-			"subscription from %s\n", sub->id);
+		ast_log(LOG_WARNING, "Unable to add datastore for MWI subscription to %s\n",
+			sub->id);
 		ao2_ref(sub, -1);
 		return NULL;
 	}
@@ -618,25 +659,26 @@ static struct mwi_subscription *mwi_create_subscription(
 static struct mwi_subscription *mwi_subscribe_single(
 	struct ast_sip_endpoint *endpoint, struct ast_sip_subscription *sip_sub, const char *name)
 {
-	RAII_VAR(struct ast_sip_aor *, aor,
-		 ast_sip_location_retrieve_aor(name), ao2_cleanup);
+	struct ast_sip_aor *aor;
 	struct mwi_subscription *sub;
 
+	aor = ast_sip_location_retrieve_aor(name);
 	if (!aor) {
 		/*! I suppose it's possible for the AOR to disappear on us
 		 * between accepting the subscription and sending the first
 		 * NOTIFY...
 		 */
-		ast_log(LOG_WARNING, "Unable to locate aor %s. MWI "
-			"subscription failed.\n", name);
+		ast_log(LOG_WARNING, "Unable to locate aor %s. MWI subscription failed.\n",
+			name);
 		return NULL;
 	}
 
-	if (!(sub = mwi_create_subscription(endpoint, sip_sub))) {
-		return NULL;
+	sub = mwi_create_subscription(endpoint, sip_sub);
+	if (sub) {
+		mwi_on_aor(aor, sub, 0);
 	}
 
-	mwi_on_aor(aor, sub, 0);
+	ao2_ref(aor, -1);
 	return sub;
 }
 
@@ -646,7 +688,6 @@ static struct mwi_subscription *mwi_subscribe_all(
 	struct mwi_subscription *sub;
 
 	sub = mwi_create_subscription(endpoint, sip_sub);
-
 	if (!sub) {
 		return NULL;
 	}
@@ -658,7 +699,7 @@ static struct mwi_subscription *mwi_subscribe_all(
 static int mwi_new_subscribe(struct ast_sip_endpoint *endpoint,
 		const char *resource)
 {
-	struct ast_sip_aor *aor;
+	RAII_VAR(struct ast_sip_aor *, aor, NULL, ao2_cleanup);
 
 	if (ast_strlen_zero(resource)) {
 		if (ast_sip_for_each_aor(endpoint->aors, mwi_validate_for_aor, endpoint)) {
@@ -668,16 +709,15 @@ static int mwi_new_subscribe(struct ast_sip_endpoint *endpoint,
 	}
 
 	aor = ast_sip_location_retrieve_aor(resource);
-
 	if (!aor) {
-		ast_log(LOG_WARNING, "Unable to locate aor %s. MWI "
-			"subscription failed.\n", resource);
+		ast_log(LOG_WARNING, "Unable to locate aor %s. MWI subscription failed.\n",
+			resource);
 		return 404;
 	}
 
 	if (ast_strlen_zero(aor->mailboxes)) {
-		ast_log(LOG_WARNING, "AOR %s has no configured mailboxes. "
-			"MWI subscription failed\n", resource);
+		ast_log(LOG_NOTICE, "AOR %s has no configured mailboxes. MWI subscription failed.\n",
+			resource);
 		return 404;
 	}
 
@@ -700,12 +740,19 @@ static int mwi_subscription_established(struct ast_sip_subscription *sip_sub)
 	} else {
 		sub = mwi_subscribe_single(endpoint, sip_sub, resource);
 	}
-
 	if (!sub) {
 		ao2_cleanup(endpoint);
 		return -1;
 	}
 
+	if (!ao2_container_count(sub->stasis_subs)) {
+		/*
+		 * We setup no MWI subscriptions so remove the MWI datastore
+		 * to break the ref loop.
+		 */
+		ast_sip_subscription_remove_datastore(sip_sub, MWI_DATASTORE);
+	}
+
 	ao2_cleanup(sub);
 	ao2_cleanup(endpoint);
 	return 0;
@@ -737,16 +784,16 @@ static void *mwi_get_notify_data(struct ast_sip_subscription *sub)
 static void mwi_subscription_mailboxes_str(struct ao2_container *stasis_subs,
 					   struct ast_str **str)
 {
-	int num = ao2_container_count(stasis_subs);
-
+	int is_first = 1;
 	struct mwi_stasis_subscription *node;
 	struct ao2_iterator i = ao2_iterator_init(stasis_subs, 0);
 
 	while ((node = ao2_iterator_next(&i))) {
-		if (--num) {
-			ast_str_append(str, 0, "%s,", node->mailbox);
-		} else {
+		if (is_first) {
+			is_first = 0;
 			ast_str_append(str, 0, "%s", node->mailbox);
+		} else {
+			ast_str_append(str, 0, ",%s", node->mailbox);
 		}
 		ao2_ref(node, -1);
 	}
@@ -757,9 +804,9 @@ static void mwi_to_ami(struct ast_sip_subscription *sub,
 		       struct ast_str **buf)
 {
 	struct mwi_subscription *mwi_sub;
-	RAII_VAR(struct ast_datastore *, mwi_datastore,
-			ast_sip_subscription_get_datastore(sub, MWI_DATASTORE), ao2_cleanup);
+	struct ast_datastore *mwi_datastore;
 
+	mwi_datastore = ast_sip_subscription_get_datastore(sub, MWI_DATASTORE);
 	if (!mwi_datastore) {
 		return;
 	}
@@ -770,6 +817,8 @@ static void mwi_to_ami(struct ast_sip_subscription *sub,
 	ast_str_append(buf, 0, "Mailboxes: ");
 	mwi_subscription_mailboxes_str(mwi_sub->stasis_subs, buf);
 	ast_str_append(buf, 0, "\r\n");
+
+	ao2_ref(mwi_datastore, -1);
 }
 
 static int serialized_notify(void *userdata)
@@ -796,36 +845,74 @@ static int serialized_cleanup(void *userdata)
 	return 0;
 }
 
+static int send_notify(void *obj, void *arg, int flags)
+{
+	struct mwi_subscription *mwi_sub = obj;
+	struct ast_taskprocessor *serializer = mwi_sub->is_solicited
+		? ast_sip_subscription_get_serializer(mwi_sub->sip_sub)
+		: NULL;
+
+	if (ast_sip_push_task(serializer, serialized_notify, ao2_bump(mwi_sub))) {
+		ao2_ref(mwi_sub, -1);
+	}
+
+	return 0;
+}
+
 static void mwi_stasis_cb(void *userdata, struct stasis_subscription *sub,
 		struct stasis_message *msg)
 {
 	struct mwi_subscription *mwi_sub = userdata;
 
 	if (stasis_subscription_final_message(sub, msg)) {
-		ao2_ref(mwi_sub, +1);
-		ast_sip_push_task(NULL, serialized_cleanup, mwi_sub);
+		if (ast_sip_push_task(NULL, serialized_cleanup, ao2_bump(mwi_sub))) {
+			ao2_ref(mwi_sub, -1);
+		}
 		return;
 	}
 
 	if (ast_mwi_state_type() == stasis_message_type(msg)) {
-		struct ast_taskprocessor *serializer = mwi_sub->is_solicited ? ast_sip_subscription_get_serializer(mwi_sub->sip_sub) : NULL;
-		ao2_ref(mwi_sub, +1);
-		ast_sip_push_task(serializer, serialized_notify, mwi_sub);
+		send_notify(mwi_sub, NULL, 0);
 	}
 }
 
+/*! \note Called with the unsolicited_mwi conainer lock held. */
 static int create_mwi_subscriptions_for_endpoint(void *obj, void *arg, int flags)
 {
 	RAII_VAR(struct mwi_subscription *, aggregate_sub, NULL, ao2_cleanup);
 	struct ast_sip_endpoint *endpoint = obj;
-	struct ao2_container *mwi_subscriptions = arg;
-	char *mailboxes;
-	char *mailbox;
+	char *endpoint_aors, *aor_name, *mailboxes, *mailbox;
+	struct ao2_container *contacts = NULL;
 
 	if (ast_strlen_zero(endpoint->subscription.mwi.mailboxes)) {
 		return 0;
 	}
 
+	endpoint_aors = ast_strdupa(endpoint->aors);
+
+	while ((aor_name = strsep(&endpoint_aors, ","))) {
+		RAII_VAR(struct ast_sip_aor *, aor, ast_sip_location_retrieve_aor(aor_name), ao2_cleanup);
+
+		if (!aor) {
+			continue;
+		}
+
+		contacts = ast_sip_location_retrieve_aor_contacts(aor);
+		if (!contacts || (ao2_container_count(contacts) == 0)) {
+			ao2_cleanup(contacts);
+			contacts = NULL;
+			continue;
+		}
+
+		break;
+	}
+
+	if (!contacts) {
+		return 0;
+	}
+
+	ao2_ref(contacts, -1);
+
 	if (endpoint->subscription.mwi.aggregate) {
 		aggregate_sub = mwi_subscription_alloc(endpoint, 0, NULL);
 		if (!aggregate_sub) {
@@ -837,18 +924,20 @@ static int create_mwi_subscriptions_for_endpoint(void *obj, void *arg, int flags
 	while ((mailbox = strsep(&mailboxes, ","))) {
 		struct mwi_subscription *sub = aggregate_sub ?:
 			mwi_subscription_alloc(endpoint, 0, NULL);
-		RAII_VAR(struct mwi_stasis_subscription *, mwi_stasis_sub,
-				mwi_stasis_subscription_alloc(mailbox, sub), ao2_cleanup);
+		struct mwi_stasis_subscription *mwi_stasis_sub;
+
+		mwi_stasis_sub = mwi_stasis_subscription_alloc(mailbox, sub);
 		if (mwi_stasis_sub) {
 			ao2_link(sub->stasis_subs, mwi_stasis_sub);
+			ao2_ref(mwi_stasis_sub, -1);
 		}
-		if (!aggregate_sub) {
-			ao2_link(mwi_subscriptions, sub);
-			ao2_cleanup(sub);
+		if (!aggregate_sub && sub) {
+			ao2_link_flags(unsolicited_mwi, sub, OBJ_NOLOCK);
+			ao2_ref(sub, -1);
 		}
 	}
 	if (aggregate_sub) {
-		ao2_link(mwi_subscriptions, aggregate_sub);
+		ao2_link_flags(unsolicited_mwi, aggregate_sub, OBJ_NOLOCK);
 	}
 	return 0;
 }
@@ -858,18 +947,17 @@ static int unsubscribe(void *obj, void *arg, int flags)
 	struct mwi_subscription *mwi_sub = obj;
 
 	ao2_callback(mwi_sub->stasis_subs, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, unsubscribe_stasis, NULL);
+
 	return CMP_MATCH;
 }
 
 static void create_mwi_subscriptions(void)
 {
-	struct ao2_container *mwi_subscriptions = ao2_container_alloc(MWI_BUCKETS, mwi_sub_hash, mwi_sub_cmp);
-	RAII_VAR(struct ao2_container *, old_mwi_subscriptions, ao2_global_obj_ref(unsolicited_mwi), ao2_cleanup);
-	RAII_VAR(struct ao2_container *, endpoints, ast_sorcery_retrieve_by_fields(
-				ast_sip_get_sorcery(), "endpoint", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL),
-			ao2_cleanup);
+	struct ao2_container *endpoints;
 
-	if (!mwi_subscriptions) {
+	endpoints = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "endpoint",
+		AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+	if (!endpoints) {
 		return;
 	}
 
@@ -879,12 +967,104 @@ static void create_mwi_subscriptions(void)
 	 * and resubscribing, up-to-date mailbox state will be sent out to the endpoint when the
 	 * new stasis subscription is established
 	 */
-	if (old_mwi_subscriptions) {
-		ao2_callback(old_mwi_subscriptions, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, unsubscribe, NULL);
+	ao2_lock(unsolicited_mwi);
+	ao2_callback(unsolicited_mwi, OBJ_NOLOCK | OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, unsubscribe, NULL);
+	ao2_callback(endpoints, OBJ_NODATA, create_mwi_subscriptions_for_endpoint, NULL);
+	ao2_unlock(unsolicited_mwi);
+
+	ao2_ref(endpoints, -1);
+}
+
+/*! \brief Function called to send MWI NOTIFY on any unsolicited mailboxes relating to this AOR */
+static int send_contact_notify(void *obj, void *arg, int flags)
+{
+	struct mwi_subscription *mwi_sub = obj;
+	const char *aor = arg;
+
+	if (!mwi_sub->aors || !strstr(mwi_sub->aors, aor)) {
+		return 0;
 	}
-	ao2_callback(endpoints, OBJ_NODATA, create_mwi_subscriptions_for_endpoint, mwi_subscriptions);
-	ao2_global_obj_replace_unref(unsolicited_mwi, mwi_subscriptions);
-	ao2_ref(mwi_subscriptions, -1);
+
+	if (ast_sip_push_task(NULL, serialized_notify, ao2_bump(mwi_sub))) {
+		ao2_ref(mwi_sub, -1);
+	}
+
+	return 0;
+}
+
+/*! \brief Function called when a contact is updated */
+static void mwi_contact_updated(const void *object)
+{
+	char *id = ast_strdupa(ast_sorcery_object_get_id(object)), *aor = NULL;
+
+	aor = strsep(&id, ";@");
+
+	ao2_callback(unsolicited_mwi, OBJ_NODATA, send_contact_notify, aor);
+}
+
+/*! \brief Function called when a contact is added */
+static void mwi_contact_added(const void *object)
+{
+	const struct ast_sip_contact *contact = object;
+	struct ao2_iterator *mwi_subs;
+	struct mwi_subscription *mwi_sub;
+	const char *endpoint_id = ast_sorcery_object_get_id(contact->endpoint);
+
+	if (ast_strlen_zero(contact->endpoint->subscription.mwi.mailboxes)) {
+		return;
+	}
+
+	ao2_lock(unsolicited_mwi);
+
+	mwi_subs = ao2_find(unsolicited_mwi, endpoint_id, OBJ_SEARCH_KEY | OBJ_MULTIPLE | OBJ_NOLOCK | OBJ_UNLINK);
+	if (mwi_subs) {
+		for (; (mwi_sub = ao2_iterator_next(mwi_subs)); ao2_cleanup(mwi_sub)) {
+			unsubscribe(mwi_sub, NULL, 0);
+		}
+		ao2_iterator_destroy(mwi_subs);
+	}
+
+	create_mwi_subscriptions_for_endpoint(contact->endpoint, NULL, 0);
+
+	ao2_unlock(unsolicited_mwi);
+
+	mwi_contact_updated(object);
+}
+
+/*! \brief Observer for contacts so unsolicited MWI is sent when a contact changes */
+static const struct ast_sorcery_observer mwi_contact_observer = {
+	.created = mwi_contact_added,
+	.updated = mwi_contact_updated,
+};
+
+/*! \brief Task invoked to send initial MWI NOTIFY for unsolicited */
+static int send_initial_notify_all(void *obj)
+{
+	ao2_callback(unsolicited_mwi, OBJ_NODATA, send_notify, NULL);
+
+	return 0;
+}
+
+/*! \brief Event callback which fires initial unsolicited MWI NOTIFY messages when we're fully booted */
+static void mwi_startup_event_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message)
+{
+	struct ast_json_payload *payload;
+	const char *type;
+
+	if (stasis_message_type(message) != ast_manager_get_generic_type()) {
+		return;
+	}
+
+	payload = stasis_message_data(message);
+	type = ast_json_string_get(ast_json_object_get(payload->json, "type"));
+
+	if (strcmp(type, "FullyBooted")) {
+		return;
+	}
+
+	ast_sip_push_task(NULL, send_initial_notify_all, NULL);
+
+	stasis_unsubscribe(sub);
 }
 
 static int reload(void)
@@ -900,17 +1080,30 @@ static int load_module(void)
 	if (ast_sip_register_subscription_handler(&mwi_handler)) {
 		return AST_MODULE_LOAD_DECLINE;
 	}
+
+	unsolicited_mwi = ao2_container_alloc(MWI_BUCKETS, mwi_sub_hash, mwi_sub_cmp);
+	if (!unsolicited_mwi) {
+		ast_sip_unregister_subscription_handler(&mwi_handler);
+		return AST_MODULE_LOAD_DECLINE;
+	}
+
 	create_mwi_subscriptions();
+	ast_sorcery_observer_add(ast_sip_get_sorcery(), "contact", &mwi_contact_observer);
+
+	if (ast_test_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED)) {
+		ast_sip_push_task(NULL, send_initial_notify_all, NULL);
+	} else {
+		stasis_subscribe_pool(ast_manager_get_topic(), mwi_startup_event_cb, NULL);
+	}
+
 	return AST_MODULE_LOAD_SUCCESS;
 }
 
 static int unload_module(void)
 {
-	RAII_VAR(struct ao2_container *, mwi_subscriptions, ao2_global_obj_ref(unsolicited_mwi), ao2_cleanup);
-	if (mwi_subscriptions) {
-		ao2_callback(mwi_subscriptions, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, unsubscribe, NULL);
-		ao2_global_obj_release(unsolicited_mwi);
-	}
+	ao2_callback(unsolicited_mwi, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, unsubscribe, NULL);
+	ao2_ref(unsolicited_mwi, -1);
+	ast_sorcery_observer_remove(ast_sip_get_sorcery(), "contact", &mwi_contact_observer);
 	ast_sip_unregister_subscription_handler(&mwi_handler);
 	return 0;
 }
diff --git a/res/res_pjsip_nat.c b/res/res_pjsip_nat.c
index 5887343..a2eb6ad 100644
--- a/res/res_pjsip_nat.c
+++ b/res/res_pjsip_nat.c
@@ -32,33 +32,89 @@
 #include "asterisk/module.h"
 #include "asterisk/acl.h"
 
-static pj_bool_t handle_rx_message(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
+static void rewrite_uri(pjsip_rx_data *rdata, pjsip_sip_uri *uri)
 {
-	pjsip_contact_hdr *contact;
+	pj_cstr(&uri->host, rdata->pkt_info.src_name);
+	if (strcasecmp("udp", rdata->tp_info.transport->type_name)) {
+		uri->transport_param = pj_str(rdata->tp_info.transport->type_name);
+	} else {
+		uri->transport_param.slen = 0;
+	}
+	uri->port = rdata->pkt_info.src_port;
+}
 
-	if (!endpoint) {
-		return PJ_FALSE;
+static int rewrite_route_set(pjsip_rx_data *rdata, pjsip_dialog *dlg)
+{
+	pjsip_rr_hdr *rr = NULL;
+	pjsip_sip_uri *uri;
+
+	if (rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG) {
+		pjsip_hdr *iter;
+		for (iter = rdata->msg_info.msg->hdr.prev; iter != &rdata->msg_info.msg->hdr; iter = iter->prev) {
+			if (iter->type == PJSIP_H_RECORD_ROUTE) {
+				rr = (pjsip_rr_hdr *)iter;
+				break;
+			}
+		}
+	} else if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_register_method)) {
+		rr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_RECORD_ROUTE, NULL);
 	}
 
-	if (endpoint->nat.rewrite_contact && (contact = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL)) &&
-		!contact->star && (PJSIP_URI_SCHEME_IS_SIP(contact->uri) || PJSIP_URI_SCHEME_IS_SIPS(contact->uri))) {
+	if (rr) {
+		uri = pjsip_uri_get_uri(&rr->name_addr);
+		rewrite_uri(rdata, uri);
+		if (dlg && !pj_list_empty(&dlg->route_set) && !dlg->route_set_frozen) {
+			pjsip_routing_hdr *route = dlg->route_set.next;
+			uri = pjsip_uri_get_uri(&route->name_addr);
+			rewrite_uri(rdata, uri);
+		}
+
+		return 0;
+	}
+
+	return -1;
+}
+
+static int rewrite_contact(pjsip_rx_data *rdata, pjsip_dialog *dlg)
+{
+	pjsip_contact_hdr *contact;
+
+	contact = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL);
+	if (contact && !contact->star && (PJSIP_URI_SCHEME_IS_SIP(contact->uri) || PJSIP_URI_SCHEME_IS_SIPS(contact->uri))) {
 		pjsip_sip_uri *uri = pjsip_uri_get_uri(contact->uri);
-		pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
 
-		pj_cstr(&uri->host, rdata->pkt_info.src_name);
-		if (strcasecmp("udp", rdata->tp_info.transport->type_name)) {
-			uri->transport_param = pj_str(rdata->tp_info.transport->type_name);
-		} else {
-			uri->transport_param.slen = 0;
-		}
-		uri->port = rdata->pkt_info.src_port;
+		rewrite_uri(rdata, uri);
 
-		/* rewrite the session target since it may have already been pulled from the contact header */
-		if (dlg && (!dlg->remote.contact
+		if (dlg && pj_list_empty(&dlg->route_set) && (!dlg->remote.contact
 			|| pjsip_uri_cmp(PJSIP_URI_IN_REQ_URI, dlg->remote.contact->uri, contact->uri))) {
 			dlg->remote.contact = (pjsip_contact_hdr*)pjsip_hdr_clone(dlg->pool, contact);
 			dlg->target = dlg->remote.contact->uri;
 		}
+		return 0;
+	}
+
+	return -1;
+}
+
+static pj_bool_t handle_rx_message(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
+{
+	pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
+
+	if (!endpoint) {
+		return PJ_FALSE;
+	}
+
+	if (endpoint->nat.rewrite_contact) {
+		/* rewrite_contact is intended to ensure we send requests/responses to
+		 * a routeable address when NAT is involved. The URI that dictates where
+		 * we send requests/responses can be determined either by Record-Route
+		 * headers or by the Contact header if no Record-Route headers are present.
+		 * We therefore will attempt to rewrite a Record-Route header first, and if
+		 * none are present, we fall back to rewriting the Contact header instead.
+		 */
+		if (rewrite_route_set(rdata, dlg)) {
+			rewrite_contact(rdata, dlg);
+		}
 	}
 
 	if (endpoint->nat.force_rport) {
@@ -70,8 +126,13 @@ static pj_bool_t handle_rx_message(struct ast_sip_endpoint *endpoint, pjsip_rx_d
 
 static pj_bool_t nat_on_rx_message(pjsip_rx_data *rdata)
 {
-	RAII_VAR(struct ast_sip_endpoint *, endpoint, ast_pjsip_rdata_get_endpoint(rdata), ao2_cleanup);
-	return handle_rx_message(endpoint, rdata);
+	pj_bool_t res;
+	struct ast_sip_endpoint *endpoint;
+
+	endpoint = ast_pjsip_rdata_get_endpoint(rdata);
+	res = handle_rx_message(endpoint, rdata);
+	ao2_cleanup(endpoint);
+	return res;
 }
 
 /*! \brief Structure which contains information about a transport */
@@ -205,6 +266,7 @@ static pj_status_t nat_on_tx_message(pjsip_tx_data *tdata)
 		pj_strdup2(tdata->pool, &uri->host, ast_sockaddr_stringify_host(&transport->external_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);
 		}
 	}
 
diff --git a/res/res_pjsip_notify.c b/res/res_pjsip_notify.c
index d7e3d80..96367cf 100644
--- a/res/res_pjsip_notify.c
+++ b/res/res_pjsip_notify.c
@@ -840,7 +840,7 @@ static char *cli_notify(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a
 		e->usage =
 			"Usage: pjsip send notify <type> {endpoint|uri} <peer> [<peer>...]\n"
 			"       Send a NOTIFY request to an endpoint\n"
-			"       Message types are defined in sip_notify.conf\n";
+			"       Message types are defined in pjsip_notify.conf\n";
 		return NULL;
 	case CLI_GENERATE:
 		if (a->argc > 4 && (!strcasecmp(a->argv[4], "uri"))) {
@@ -1021,6 +1021,7 @@ static int unload_module(void)
 	ast_manager_unregister("PJSIPNotify");
 	ast_cli_unregister_multiple(cli_options, ARRAY_LEN(cli_options));
 	aco_info_destroy(&notify_cfg);
+	ao2_global_obj_release(globals);
 
 	return 0;
 }
diff --git a/res/res_pjsip_outbound_authenticator_digest.c b/res/res_pjsip_outbound_authenticator_digest.c
index 64238a8..8616b6c 100644
--- a/res/res_pjsip_outbound_authenticator_digest.c
+++ b/res/res_pjsip_outbound_authenticator_digest.c
@@ -101,13 +101,14 @@ cleanup:
 	return res;
 }
 
-static int digest_create_request_with_auth(const struct ast_sip_auth_vector *auths, pjsip_rx_data *challenge,
-		pjsip_transaction *tsx, pjsip_tx_data **new_request)
+static int digest_create_request_with_auth_from_old(const struct ast_sip_auth_vector *auths, pjsip_rx_data *challenge,
+		pjsip_tx_data *old_request, pjsip_tx_data **new_request)
 {
 	pjsip_auth_clt_sess auth_sess;
+	pjsip_cseq_hdr *cseq;
 
 	if (pjsip_auth_clt_init(&auth_sess, ast_sip_get_pjsip_endpoint(),
-				tsx->pool, 0) != PJ_SUCCESS) {
+				old_request->pool, 0) != PJ_SUCCESS) {
 		ast_log(LOG_WARNING, "Failed to initialize client authentication session\n");
 		return -1;
 	}
@@ -118,8 +119,17 @@ static int digest_create_request_with_auth(const struct ast_sip_auth_vector *aut
 	}
 
 	switch (pjsip_auth_clt_reinit_req(&auth_sess, challenge,
-				tsx->last_tx, new_request)) {
+				old_request, new_request)) {
 	case PJ_SUCCESS:
+		/* PJSIP creates a new transaction for new_request (meaning it creates a new
+		 * branch). However, it recycles the Call-ID, from-tag, and CSeq from the
+		 * original request. Some SIP implementations will not process the new request
+		 * since the CSeq is the same as the original request. Incrementing it here
+		 * fixes the interop issue
+		 */
+		cseq = pjsip_msg_find_hdr((*new_request)->msg, PJSIP_H_CSEQ, NULL);
+		ast_assert(cseq != NULL);
+		++cseq->cseq;
 		return 0;
 	case PJSIP_ENOCREDENTIAL:
 		ast_log(LOG_WARNING, "Unable to create request with auth."
@@ -140,8 +150,15 @@ static int digest_create_request_with_auth(const struct ast_sip_auth_vector *aut
 	return -1;
 }
 
+static int digest_create_request_with_auth(const struct ast_sip_auth_vector *auths, pjsip_rx_data *challenge,
+		pjsip_transaction *tsx, pjsip_tx_data **new_request)
+{
+	return digest_create_request_with_auth_from_old(auths, challenge, tsx->last_tx, new_request);
+}
+
 static struct ast_sip_outbound_authenticator digest_authenticator = {
 	.create_request_with_auth = digest_create_request_with_auth,
+	.create_request_with_auth_from_old = digest_create_request_with_auth_from_old,
 };
 
 static int load_module(void)
diff --git a/res/res_pjsip_outbound_publish.c b/res/res_pjsip_outbound_publish.c
index 8bf329a..8b6f6e4 100644
--- a/res/res_pjsip_outbound_publish.c
+++ b/res/res_pjsip_outbound_publish.c
@@ -105,26 +105,6 @@ struct sip_outbound_publish_message {
 	char body_contents[0];
 };
 
-/*! \brief Outbound publish client state information (persists for lifetime that publish should exist) */
-struct ast_sip_outbound_publish_client {
-	/*! \brief Underlying publish client */
-	pjsip_publishc *client;
-	/*! \brief Timer entry for refreshing publish */
-	pj_timer_entry timer;
-	/*! \brief Publisher datastores set up by handlers */
-	struct ao2_container *datastores;
-	/*! \brief The number of auth attempts done */
-	unsigned int auth_attempts;
-	/*! \brief Queue of outgoing publish messages to send*/
-	AST_LIST_HEAD_NOLOCK(, sip_outbound_publish_message) queue;
-	/*! \brief The message currently being sent */
-	struct sip_outbound_publish_message *sending;
-	/*! \brief Publish client has been fully started and event type informed */
-	unsigned int started;
-	/*! \brief Publish client should be destroyed */
-	unsigned int destroy;
-};
-
 /*! \brief Outbound publish information */
 struct ast_sip_outbound_publish {
 	/*! \brief Sorcery object details */
@@ -148,14 +128,122 @@ struct ast_sip_outbound_publish {
 	unsigned int max_auth_attempts;
 	/*! \brief Configured authentication credentials */
 	struct ast_sip_auth_vector outbound_auths;
-	/*! \brief Outbound publish state */
-	struct ast_sip_outbound_publish_client *state;
 };
 
-AST_RWLIST_HEAD_STATIC(publisher_handlers, ast_sip_event_publisher_handler);
+/*! \brief Outbound publish client state information (persists for lifetime that publish should exist) */
+struct ast_sip_outbound_publish_client {
+	/*! \brief Underlying publish client */
+	pjsip_publishc *client;
+	/*! \brief Timer entry for refreshing publish */
+	pj_timer_entry timer;
+	/*! \brief Publisher datastores set up by handlers */
+	struct ao2_container *datastores;
+	/*! \brief The number of auth attempts done */
+	unsigned int auth_attempts;
+	/*! \brief Queue of outgoing publish messages to send*/
+	AST_LIST_HEAD_NOLOCK(, sip_outbound_publish_message) queue;
+	/*! \brief The message currently being sent */
+	struct sip_outbound_publish_message *sending;
+	/*! \brief Publish client has been fully started and event type informed */
+	unsigned int started;
+	/*! \brief Publish client should be destroyed */
+	unsigned int destroy;
+	/*! \brief Outbound publish information */
+	struct ast_sip_outbound_publish *publish;
+};
+
+/*! \brief Outbound publish state information (persists for lifetime of a publish) */
+struct ast_sip_outbound_publish_state {
+	/*! \brief Outbound publish client */
+	struct ast_sip_outbound_publish_client *client;
+	/* publish state id lookup key - same as publish configuration id */
+	char id[0];
+};
+
+/*! \brief Unloading data */
+struct unloading_data {
+	int is_unloading;
+	int count;
+	ast_mutex_t lock;
+	ast_cond_t cond;
+} unloading;
+
+/*! \brief Default number of client state container buckets */
+#define DEFAULT_STATE_BUCKETS 31
+static AO2_GLOBAL_OBJ_STATIC(current_states);
+/*! \brief Used on [re]loads to hold new state data */
+static struct ao2_container *new_states;
+
+/*! \brief hashing function for state objects */
+static int outbound_publish_state_hash(const void *obj, const int flags)
+{
+	const struct ast_sip_outbound_publish_state *object;
+	const char *key;
+
+	switch (flags & OBJ_SEARCH_MASK) {
+	case OBJ_SEARCH_KEY:
+		key = obj;
+		break;
+	case OBJ_SEARCH_OBJECT:
+		object = obj;
+		key = object->id;
+		break;
+	default:
+		ast_assert(0);
+		return 0;
+	}
+	return ast_str_hash(key);
+}
+
+/*! \brief comparator function for client objects */
+static int outbound_publish_state_cmp(void *obj, void *arg, int flags)
+{
+	const struct ast_sip_outbound_publish_state *object_left = obj;
+	const struct ast_sip_outbound_publish_state *object_right = arg;
+	const char *right_key = arg;
+	int cmp;
+
+	switch (flags & OBJ_SEARCH_MASK) {
+	case OBJ_SEARCH_OBJECT:
+		right_key = object_right->id;
+		/* Fall through */
+	case OBJ_SEARCH_KEY:
+		cmp = strcmp(object_left->id, right_key);
+		break;
+	case OBJ_SEARCH_PARTIAL_KEY:
+		/* Not supported by container. */
+		ast_assert(0);
+		return 0;
+	default:
+		cmp = 0;
+		break;
+	}
+	if (cmp) {
+		return 0;
+	}
+	return CMP_MATCH;
+}
 
-/*! \brief Container of currently active publish clients */
-static AO2_GLOBAL_OBJ_STATIC(active);
+static struct ao2_container *get_publishes_and_update_state(void)
+{
+	struct ao2_container *container;
+
+	container = ast_sorcery_retrieve_by_fields(
+		ast_sip_get_sorcery(), "outbound-publish",
+		AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+
+	if (!new_states) {
+		return container;
+	}
+
+	ao2_global_obj_replace_unref(current_states, new_states);
+	ao2_cleanup(new_states);
+	new_states = NULL;
+
+	return container;
+}
+
+AST_RWLIST_HEAD_STATIC(publisher_handlers, ast_sip_event_publisher_handler);
 
 static void sub_add_handler(struct ast_sip_event_publisher_handler *handler)
 {
@@ -185,17 +273,15 @@ static void cancel_publish_refresh(struct ast_sip_outbound_publish_client *clien
 }
 
 /*! \brief Helper function which sets up the timer to send publication */
-static void schedule_publish_refresh(struct ast_sip_outbound_publish *publish, pjsip_rx_data *rdata)
+static void schedule_publish_refresh(struct ast_sip_outbound_publish_client *client, int expiration)
 {
+	struct ast_sip_outbound_publish *publish = ao2_bump(client->publish);
 	pj_time_val delay = { .sec = 0, };
-	pjsip_expires_hdr *expires;
 
-	cancel_publish_refresh(publish->state);
+	cancel_publish_refresh(client);
 
-	/* Determine when we should refresh - we favor the Expires header if possible */
-	expires = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
-	if (expires) {
-		delay.sec = expires->ivalue - PJSIP_PUBLISHC_DELAY_BEFORE_REFRESH;
+	if (expiration > 0) {
+		delay.sec = expiration - PJSIP_PUBLISHC_DELAY_BEFORE_REFRESH;
 	}
 	if (publish->expiration && ((delay.sec > publish->expiration) || !delay.sec)) {
 		delay.sec = publish->expiration;
@@ -204,11 +290,12 @@ static void schedule_publish_refresh(struct ast_sip_outbound_publish *publish, p
 		delay.sec = PJSIP_PUBLISHC_DELAY_BEFORE_REFRESH;
 	}
 
-	ao2_ref(publish->state, +1);
-	if (pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(), &publish->state->timer, &delay) != PJ_SUCCESS) {
+	ao2_ref(client, +1);
+	if (pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(), &client->timer, &delay) != PJ_SUCCESS) {
 		ast_log(LOG_WARNING, "Failed to pass timed publish refresh to scheduler\n");
-		ao2_ref(publish->state, -1);
+		ao2_ref(client, -1);
 	}
+	ao2_ref(publish, -1);
 }
 
 /*! \brief Publish client timer callback function */
@@ -229,10 +316,10 @@ static void sip_outbound_publish_timer_cb(pj_timer_heap_t *timer_heap, struct pj
 /*! \brief Task for cancelling a refresh timer */
 static int cancel_refresh_timer_task(void *data)
 {
-	struct ast_sip_outbound_publish_client *state = data;
+	struct ast_sip_outbound_publish_client *client = data;
 
-	cancel_publish_refresh(state);
-	ao2_ref(state, -1);
+	cancel_publish_refresh(client);
+	ao2_ref(client, -1);
 
 	return 0;
 }
@@ -240,14 +327,14 @@ static int cancel_refresh_timer_task(void *data)
 /*! \brief Task for sending an unpublish */
 static int send_unpublish_task(void *data)
 {
-	struct ast_sip_outbound_publish_client *state = data;
+	struct ast_sip_outbound_publish_client *client = data;
 	pjsip_tx_data *tdata;
 
-	if (pjsip_publishc_unpublish(state->client, &tdata) == PJ_SUCCESS) {
-		pjsip_publishc_send(state->client, tdata);
+	if (pjsip_publishc_unpublish(client->client, &tdata) == PJ_SUCCESS) {
+		pjsip_publishc_send(client->client, tdata);
 	}
 
-	ao2_ref(state, -1);
+	ao2_ref(client, -1);
 
 	return 0;
 }
@@ -255,53 +342,70 @@ static int send_unpublish_task(void *data)
 /*! \brief Helper function which starts or stops publish clients when applicable */
 static void sip_outbound_publish_synchronize(struct ast_sip_event_publisher_handler *removed)
 {
-	RAII_VAR(struct ao2_container *, publishes, ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "outbound-publish", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL), ao2_cleanup);
+	RAII_VAR(struct ao2_container *, publishes, get_publishes_and_update_state(), ao2_cleanup);
+	struct ao2_container *states;
 	struct ao2_iterator i;
-	struct ast_sip_outbound_publish *publish;
+	struct ast_sip_outbound_publish_state *state;
 
 	if (!publishes) {
 		return;
 	}
 
-	i = ao2_iterator_init(publishes, 0);
-	while ((publish = ao2_iterator_next(&i))) {
+	states = ao2_global_obj_ref(current_states);
+	if (!states) {
+		return;
+	}
+
+	i = ao2_iterator_init(states, 0);
+	while ((state = ao2_iterator_next(&i))) {
+		struct ast_sip_outbound_publish *publish = ao2_bump(state->client->publish);
 		struct ast_sip_event_publisher_handler *handler = find_publisher_handler_for_event_name(publish->event);
 
-		if (!publish->state->started) {
+		if (!state->client->started) {
 			/* If the publisher client has not yet been started try to start it */
 			if (!handler) {
 				ast_debug(2, "Could not find handler for event '%s' for outbound publish client '%s'\n",
-					publish->event, ast_sorcery_object_get_id(publish));
-			} else if (handler->start_publishing(publish, publish->state)) {
+					  publish->event, ast_sorcery_object_get_id(publish));
+			} else if (handler->start_publishing(publish, state->client)) {
 				ast_log(LOG_ERROR, "Failed to start outbound publish with event '%s' for client '%s'\n",
 					publish->event, ast_sorcery_object_get_id(publish));
 			} else {
-				publish->state->started = 1;
+				state->client->started = 1;
 			}
-		} else if (publish->state->started && !handler && removed && !strcmp(publish->event, removed->event_name)) {
+		} else if (state->client->started && !handler && removed && !strcmp(publish->event, removed->event_name)) {
 			/* If the publisher client has been started but it is going away stop it */
-			removed->stop_publishing(publish->state);
-			publish->state->started = 0;
-			if (ast_sip_push_task(NULL, cancel_refresh_timer_task, ao2_bump(publish->state))) {
+			removed->stop_publishing(state->client);
+			state->client->started = 0;
+			if (ast_sip_push_task(NULL, cancel_refresh_timer_task, ao2_bump(state->client))) {
 				ast_log(LOG_WARNING, "Could not stop refresh timer on client '%s'\n",
 					ast_sorcery_object_get_id(publish));
-				ao2_ref(publish->state, -1);
+				ao2_ref(state->client, -1);
 			}
 		}
 		ao2_ref(publish, -1);
+		ao2_ref(state, -1);
 	}
 	ao2_iterator_destroy(&i);
+	ao2_ref(states, -1);
 }
 
 struct ast_sip_outbound_publish_client *ast_sip_publish_client_get(const char *name)
 {
-	RAII_VAR(struct ast_sip_outbound_publish *, publish, ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "outbound-publish", name), ao2_cleanup);
+	RAII_VAR(struct ao2_container *, states,
+		 ao2_global_obj_ref(current_states), ao2_cleanup);
+	RAII_VAR(struct ast_sip_outbound_publish_state *, state, NULL, ao2_cleanup);
 
-	if (!publish) {
+	if (!states) {
 		return NULL;
 	}
 
-	return ao2_bump(publish->state);
+	state = ao2_find(states, name, OBJ_SEARCH_KEY);
+	if (!state) {
+		return NULL;
+	}
+
+	ao2_ref(state->client, +1);
+	return state->client;
 }
 
 int ast_sip_register_event_publisher_handler(struct ast_sip_event_publisher_handler *handler)
@@ -351,16 +455,7 @@ void ast_sip_unregister_event_publisher_handler(struct ast_sip_event_publisher_h
 static void sip_outbound_publish_destroy(void *obj)
 {
 	struct ast_sip_outbound_publish *publish = obj;
-	SCOPED_LOCK(lock, &publisher_handlers, AST_RWLIST_RDLOCK, AST_RWLIST_UNLOCK);
-	struct ast_sip_event_publisher_handler *handler = find_publisher_handler_for_event_name(publish->event);
 
-	if (handler) {
-		handler->stop_publishing(publish->state);
-	}
-	if (publish->state) {
-		cancel_publish_refresh(publish->state);
-		ao2_ref(publish->state, -1);
-	}
 	ast_sip_auth_vector_destroy(&publish->outbound_auths);
 
 	ast_string_field_free_memory(publish);
@@ -538,19 +633,87 @@ int ast_sip_publish_client_send(struct ast_sip_outbound_publish_client *client,
 	return res;
 }
 
-/*! \brief Destructor function for publish state */
+/*! \brief Destructor function for publish client */
 static void sip_outbound_publish_client_destroy(void *obj)
 {
-	struct ast_sip_outbound_publish_client *state = obj;
+	struct ast_sip_outbound_publish_client *client = obj;
 	struct sip_outbound_publish_message *message;
 
 	/* You might be tempted to think "the publish client isn't being destroyed" but it actually is - just elsewhere */
 
-	while ((message = AST_LIST_REMOVE_HEAD(&state->queue, entry))) {
+	while ((message = AST_LIST_REMOVE_HEAD(&client->queue, entry))) {
 		ast_free(message);
 	}
 
-	ao2_cleanup(state->datastores);
+	ao2_cleanup(client->datastores);
+	ao2_cleanup(client->publish);
+
+	/* if unloading the module and all objects have been unpublished
+	   send the signal to finish unloading */
+	if (unloading.is_unloading) {
+		ast_mutex_lock(&unloading.lock);
+		if (--unloading.count == 0) {
+			ast_cond_signal(&unloading.cond);
+		}
+		ast_mutex_unlock(&unloading.lock);
+	}
+}
+
+static int explicit_publish_destroy(void *data)
+{
+	struct ast_sip_outbound_publish_client *client = data;
+
+	pjsip_publishc_destroy(client->client);
+	ao2_ref(client, -1);
+
+	return 0;
+}
+
+/*! \brief Helper function which cancels and un-publishes a no longer used client */
+static int cancel_and_unpublish(struct ast_sip_outbound_publish_client *client)
+{
+	struct ast_sip_event_publisher_handler *handler;
+	SCOPED_AO2LOCK(lock, client);
+
+	if (!client->started) {
+		/* If the client was never started, there's nothing to unpublish, so just
+		 * destroy the publication and remove its reference to the client.
+		 */
+		ast_sip_push_task(NULL, explicit_publish_destroy, client);
+		return 0;
+	}
+
+	handler = find_publisher_handler_for_event_name(client->publish->event);
+	if (handler) {
+		handler->stop_publishing(client);
+	}
+
+	client->started = 0;
+	if (ast_sip_push_task(NULL, cancel_refresh_timer_task, ao2_bump(client))) {
+		ast_log(LOG_WARNING, "Could not stop refresh timer on outbound publish '%s'\n",
+			ast_sorcery_object_get_id(client->publish));
+		ao2_ref(client, -1);
+	}
+
+	/* If nothing is being sent right now send the unpublish - the destroy will happen in the subsequent callback */
+	if (!client->sending) {
+		if (ast_sip_push_task(NULL, send_unpublish_task, ao2_bump(client))) {
+			ast_log(LOG_WARNING, "Could not send unpublish message on outbound publish '%s'\n",
+				ast_sorcery_object_get_id(client->publish));
+			ao2_ref(client, -1);
+		}
+	}
+	client->destroy = 1;
+	return 0;
+}
+
+/*! \brief Destructor function for publish state */
+static void sip_outbound_publish_state_destroy(void *obj)
+{
+	struct ast_sip_outbound_publish_state *state = obj;
+
+	cancel_and_unpublish(state->client);
+	ao2_cleanup(state->client);
 }
 
 /*!
@@ -588,35 +751,38 @@ static void sip_outbound_publish_callback(struct pjsip_publishc_cbparam *param);
 /*! \brief Helper function that allocates a pjsip publish client and configures it */
 static int sip_outbound_publish_client_alloc(void *data)
 {
-	struct ast_sip_outbound_publish *publish = data;
+	struct ast_sip_outbound_publish_client *client = data;
+	RAII_VAR(struct ast_sip_outbound_publish *, publish, NULL, ao2_cleanup);
 	pjsip_publishc_opt opt = {
 		.queue_request = PJ_FALSE,
 	};
 	pj_str_t event, server_uri, to_uri, from_uri;
 	pj_status_t status;
 
-	if (publish->state->client) {
+	if (client->client) {
 		return 0;
-	} else if (pjsip_publishc_create(ast_sip_get_pjsip_endpoint(), &opt, ao2_bump(publish), sip_outbound_publish_callback,
-		&publish->state->client) != PJ_SUCCESS) {
-		ao2_ref(publish, -1);
+	} else if (pjsip_publishc_create(ast_sip_get_pjsip_endpoint(), &opt, ao2_bump(client), sip_outbound_publish_callback,
+		&client->client) != PJ_SUCCESS) {
+		ao2_ref(client, -1);
 		return -1;
 	}
 
+	publish = ao2_bump(client->publish);
+
 	if (!ast_strlen_zero(publish->outbound_proxy)) {
 		pjsip_route_hdr route_set, *route;
 		static const pj_str_t ROUTE_HNAME = { "Route", 5 };
 
 		pj_list_init(&route_set);
 
-		if (!(route = pjsip_parse_hdr(pjsip_publishc_get_pool(publish->state->client), &ROUTE_HNAME,
+		if (!(route = pjsip_parse_hdr(pjsip_publishc_get_pool(client->client), &ROUTE_HNAME,
 			(char*)publish->outbound_proxy, strlen(publish->outbound_proxy), NULL))) {
-			pjsip_publishc_destroy(publish->state->client);
+			pjsip_publishc_destroy(client->client);
 			return -1;
 		}
 		pj_list_insert_nodes_before(&route_set, route);
 
-		pjsip_publishc_set_route_set(publish->state->client, &route_set);
+		pjsip_publishc_set_route_set(client->client, &route_set);
 	}
 
 	pj_cstr(&event, publish->event);
@@ -624,7 +790,7 @@ static int sip_outbound_publish_client_alloc(void *data)
 	pj_cstr(&to_uri, S_OR(publish->to_uri, publish->server_uri));
 	pj_cstr(&from_uri, S_OR(publish->from_uri, publish->server_uri));
 
-	status = pjsip_publishc_init(publish->state->client, &event, &server_uri, &from_uri, &to_uri,
+	status = pjsip_publishc_init(client->client, &event, &server_uri, &from_uri, &to_uri,
 		publish->expiration);
 	if (status == PJSIP_EINVALIDURI) {
 		pj_pool_t *pool;
@@ -635,7 +801,7 @@ static int sip_outbound_publish_client_alloc(void *data)
 		if (!pool) {
 			ast_log(LOG_ERROR, "Could not create pool for URI validation on outbound publish '%s'\n",
 				ast_sorcery_object_get_id(publish));
-			pjsip_publishc_destroy(publish->state->client);
+			pjsip_publishc_destroy(client->client);
 			return -1;
 		}
 
@@ -665,10 +831,10 @@ static int sip_outbound_publish_client_alloc(void *data)
 		}
 
 		pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
-		pjsip_publishc_destroy(publish->state->client);
+		pjsip_publishc_destroy(client->client);
 		return -1;
 	} else if (status != PJ_SUCCESS) {
-		pjsip_publishc_destroy(publish->state->client);
+		pjsip_publishc_destroy(client->client);
 		return -1;
 	}
 
@@ -678,51 +844,52 @@ static int sip_outbound_publish_client_alloc(void *data)
 /*! \brief Callback function for publish client responses */
 static void sip_outbound_publish_callback(struct pjsip_publishc_cbparam *param)
 {
-	RAII_VAR(struct ast_sip_outbound_publish *, publish, ao2_bump(param->token), ao2_cleanup);
-	SCOPED_AO2LOCK(lock, publish->state);
+	RAII_VAR(struct ast_sip_outbound_publish_client *, client, ao2_bump(param->token), ao2_cleanup);
+	RAII_VAR(struct ast_sip_outbound_publish *, publish, ao2_bump(client->publish), ao2_cleanup);
+	SCOPED_AO2LOCK(lock, client);
 	pjsip_tx_data *tdata;
 
-	if (publish->state->destroy) {
-		if (publish->state->sending) {
-			publish->state->sending = NULL;
-			if (!ast_sip_push_task(NULL, send_unpublish_task, ao2_bump(publish->state))) {
+	if (client->destroy) {
+		if (client->sending) {
+			client->sending = NULL;
+
+			if (!ast_sip_push_task(NULL, send_unpublish_task, ao2_bump(client))) {
 				return;
 			}
 			ast_log(LOG_WARNING, "Could not send unpublish message on outbound publish '%s'\n",
 				ast_sorcery_object_get_id(publish));
-			ao2_ref(publish->state, -1);
+			ao2_ref(client, -1);
 		}
-		/* Once the destroy is called this callback will not get called any longer, so drop the publish ref */
-		pjsip_publishc_destroy(publish->state->client);
-		ao2_ref(publish, -1);
+		/* Once the destroy is called this callback will not get called any longer, so drop the client ref */
+		pjsip_publishc_destroy(client->client);
+		ao2_ref(client, -1);
 		return;
 	}
 
 	if (param->code == 401 || param->code == 407) {
 		if (!ast_sip_create_request_with_auth(&publish->outbound_auths,
 				param->rdata, pjsip_rdata_get_tsx(param->rdata), &tdata)) {
-			pjsip_publishc_send(publish->state->client, tdata);
+			pjsip_publishc_send(client->client, tdata);
 		}
-		publish->state->auth_attempts++;
+		client->auth_attempts++;
 
-		if (publish->state->auth_attempts == publish->max_auth_attempts) {
-			pjsip_publishc_destroy(publish->state->client);
-			publish->state->client = NULL;
+		if (client->auth_attempts == publish->max_auth_attempts) {
+			pjsip_publishc_destroy(client->client);
+			client->client = NULL;
 
 			ast_log(LOG_ERROR, "Reached maximum number of PUBLISH authentication attempts on outbound publish '%s'\n",
 				ast_sorcery_object_get_id(publish));
 
 			goto end;
 		}
-
 		return;
 	}
 
-	publish->state->auth_attempts = 0;
+	client->auth_attempts = 0;
 
 	if (param->code == 412) {
-		pjsip_publishc_destroy(publish->state->client);
-		publish->state->client = NULL;
+		pjsip_publishc_destroy(client->client);
+		client->client = NULL;
 
 		if (sip_outbound_publish_client_alloc(publish)) {
 			ast_log(LOG_ERROR, "Failed to create a new outbound publish client for '%s' on 412 response\n",
@@ -731,7 +898,7 @@ static void sip_outbound_publish_callback(struct pjsip_publishc_cbparam *param)
 		}
 
 		/* Setting this to NULL will cause a new PUBLISH to get created and sent for the same underlying body */
-		publish->state->sending = NULL;
+		client->sending = NULL;
 	} else if (param->code == 423) {
 		/* Update the expiration with the new expiration time if available */
 		pjsip_expires_hdr *expires;
@@ -740,34 +907,38 @@ static void sip_outbound_publish_callback(struct pjsip_publishc_cbparam *param)
 		if (!expires || !expires->ivalue) {
 			ast_log(LOG_ERROR, "Received 423 response on outbound publish '%s' without a Min-Expires header\n",
 				ast_sorcery_object_get_id(publish));
-			pjsip_publishc_destroy(publish->state->client);
-			publish->state->client = NULL;
+			pjsip_publishc_destroy(client->client);
+			client->client = NULL;
 			goto end;
 		}
 
-		pjsip_publishc_update_expires(publish->state->client, expires->ivalue);
-		publish->state->sending = NULL;
-	} else if (publish->state->sending) {
+		pjsip_publishc_update_expires(client->client, expires->ivalue);
+		client->sending = NULL;
+	} else if (client->sending) {
 		/* Remove the message currently being sent so that when the queue is serviced another will get sent */
-		AST_LIST_REMOVE_HEAD(&publish->state->queue, entry);
-		ast_free(publish->state->sending);
-		publish->state->sending = NULL;
+		AST_LIST_REMOVE_HEAD(&client->queue, entry);
+		ast_free(client->sending);
+		client->sending = NULL;
+		if (!param->rdata) {
+			ast_log(LOG_NOTICE, "No response received for outbound publish '%s'\n",
+				ast_sorcery_object_get_id(publish));
+		}
 	}
 
-	if (AST_LIST_EMPTY(&publish->state->queue)) {
-		schedule_publish_refresh(publish, param->rdata);
+	if (AST_LIST_EMPTY(&client->queue)) {
+		schedule_publish_refresh(client, param->expiration);
 	}
 
 end:
-	if (!publish->state->client) {
+	if (!client->client) {
 		struct sip_outbound_publish_message *message;
 
-		while ((message = AST_LIST_REMOVE_HEAD(&publish->state->queue, entry))) {
+		while ((message = AST_LIST_REMOVE_HEAD(&client->queue, entry))) {
 			ast_free(message);
 		}
 	} else {
-		if (ast_sip_push_task(NULL, sip_publish_client_service_queue, ao2_bump(publish->state))) {
-			ao2_ref(publish->state, -1);
+		if (ast_sip_push_task(NULL, sip_publish_client_service_queue, ao2_bump(client))) {
+			ao2_ref(client, -1);
 		}
 	}
 }
@@ -831,31 +1002,43 @@ static int datastore_cmp(void *obj, void *arg, int flags)
 	return CMP_MATCH;
 }
 
-/*! \brief Allocator function for publish client state */
-static struct ast_sip_outbound_publish_client *sip_outbound_publish_state_alloc(void)
+/*! \brief Allocator function for publish client */
+static struct ast_sip_outbound_publish_state *sip_outbound_publish_state_alloc(
+	struct ast_sip_outbound_publish *publish)
 {
-	struct ast_sip_outbound_publish_client *state = ao2_alloc(sizeof(*state), sip_outbound_publish_client_destroy);
+	const char *id = ast_sorcery_object_get_id(publish);
+	struct ast_sip_outbound_publish_state *state =
+		ao2_alloc(sizeof(*state) + strlen(id) + 1, sip_outbound_publish_state_destroy);
 
 	if (!state) {
 		return NULL;
 	}
 
-	state->datastores = ao2_container_alloc(DATASTORE_BUCKETS, datastore_hash, datastore_cmp);
-	if (!state->datastores) {
+	state->client = ao2_alloc(sizeof(*state->client), sip_outbound_publish_client_destroy);
+	if (!state->client) {
 		ao2_ref(state, -1);
 		return NULL;
 	}
 
-	state->timer.user_data = state;
-	state->timer.cb = sip_outbound_publish_timer_cb;
+	state->client->datastores = ao2_container_alloc(DATASTORE_BUCKETS, datastore_hash, datastore_cmp);
+	if (!state->client->datastores) {
+		ao2_ref(state, -1);
+		return NULL;
+	}
 
+	state->client->timer.user_data = state->client;
+	state->client->timer.cb = sip_outbound_publish_timer_cb;
+	state->client->publish = ao2_bump(publish);
+
+	strcpy(state->id, id);
 	return state;
 }
 
 /*! \brief Apply function which finds or allocates a state structure */
 static int sip_outbound_publish_apply(const struct ast_sorcery *sorcery, void *obj)
 {
-	RAII_VAR(struct ast_sip_outbound_publish *, existing, ast_sorcery_retrieve_by_id(sorcery, "outbound-publish", ast_sorcery_object_get_id(obj)), ao2_cleanup);
+	RAII_VAR(struct ao2_container *, states, ao2_global_obj_ref(current_states), ao2_cleanup);
+	RAII_VAR(struct ast_sip_outbound_publish_state *, state, NULL, ao2_cleanup);
 	struct ast_sip_outbound_publish *applied = obj;
 
 	if (ast_strlen_zero(applied->server_uri)) {
@@ -868,24 +1051,47 @@ static int sip_outbound_publish_apply(const struct ast_sorcery *sorcery, void *o
 		return -1;
 	}
 
-	if (!existing) {
-		/* If no existing publish exists we can just start fresh easily */
-		applied->state = sip_outbound_publish_state_alloc();
-	} else {
-		/* If there is an existing publish things are more complicated, we can immediately reuse this state if most stuff remains unchanged */
-		if (can_reuse_publish(existing, applied)) {
-			applied->state = existing->state;
-			ao2_ref(applied->state, +1);
-		} else {
-			applied->state = sip_outbound_publish_state_alloc();
+	if (!new_states) {
+		/* make sure new_states has been allocated as we will be adding to it */
+		new_states = ao2_container_alloc_options(
+			AO2_ALLOC_OPT_LOCK_NOLOCK, DEFAULT_STATE_BUCKETS,
+			outbound_publish_state_hash, outbound_publish_state_cmp);
+
+		if (!new_states) {
+			ast_log(LOG_ERROR, "Unable to allocate new states container\n");
+			return -1;
 		}
 	}
 
-	if (!applied->state) {
+	if (states) {
+		state = ao2_find(states, ast_sorcery_object_get_id(obj), OBJ_SEARCH_KEY);
+		if (state) {
+			if (can_reuse_publish(state->client->publish, applied)) {
+				ao2_replace(state->client->publish, applied);
+			} else {
+				ao2_ref(state, -1);
+				state = NULL;
+			}
+		}
+	}
+
+	if (!state) {
+		state = sip_outbound_publish_state_alloc(applied);
+		if (!state) {
+			ast_log(LOG_ERROR, "Unable to create state for outbound publish '%s'\n",
+				ast_sorcery_object_get_id(applied));
+			return -1;
+		};
+	}
+
+	if (ast_sip_push_task_synchronous(NULL, sip_outbound_publish_client_alloc, state->client)) {
+		ast_log(LOG_ERROR, "Unable to create client for outbound publish '%s'\n",
+			ast_sorcery_object_get_id(applied));
 		return -1;
 	}
 
-	return ast_sip_push_task_synchronous(NULL, sip_outbound_publish_client_alloc, applied);
+	ao2_link(new_states, state);
+	return 0;
 }
 
 static int outbound_auth_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
@@ -895,74 +1101,11 @@ static int outbound_auth_handler(const struct aco_option *opt, struct ast_variab
 	return ast_sip_auth_vector_init(&publish->outbound_auths, var->value);
 }
 
-/*! \brief Helper function which prunes old publish clients */
-static void prune_publish_clients(const char *object_type)
-{
-	struct ao2_container *old, *current;
-
-	old = ao2_global_obj_ref(active);
-	if (old) {
-		struct ao2_iterator i;
-		struct ast_sip_outbound_publish *existing;
-
-		i = ao2_iterator_init(old, 0);
-		for (; (existing = ao2_iterator_next(&i)); ao2_ref(existing, -1)) {
-			struct ast_sip_outbound_publish *created;
-
-			created = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "outbound-publish",
-				ast_sorcery_object_get_id(existing));
-			if (created) {
-				if (created->state == existing->state) {
-					ao2_ref(created, -1);
-					continue;
-				}
-				ao2_ref(created, -1);
-			}
-
-			ao2_lock(existing->state);
-
-			/* If this publish client is currently publishing stop and terminate any refresh timer */
-			if (existing->state->started) {
-				struct ast_sip_event_publisher_handler *handler = find_publisher_handler_for_event_name(existing->event);
-
-				if (handler) {
-					handler->stop_publishing(existing->state);
-				}
-
-				if (ast_sip_push_task(NULL, cancel_refresh_timer_task, ao2_bump(existing->state))) {
-					ast_log(LOG_WARNING, "Could not stop refresh timer on outbound publish '%s'\n",
-						ast_sorcery_object_get_id(existing));
-					ao2_ref(existing->state, -1);
-				}
-			}
-
-			/* If nothing is being sent right now send the unpublish - the destroy will happen in the subsequent callback */
-			if (!existing->state->sending) {
-				if (ast_sip_push_task(NULL, send_unpublish_task, ao2_bump(existing->state))) {
-					ast_log(LOG_WARNING, "Could not send unpublish message on outbound publish '%s'\n",
-						ast_sorcery_object_get_id(existing));
-					ao2_ref(existing->state, -1);
-				}
-			}
-
-			existing->state->destroy = 1;
-			ao2_unlock(existing->state);
-		}
-		ao2_iterator_destroy(&i);
-
-		ao2_ref(old, -1);
-	}
-
-	current = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "outbound-publish", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
-	ao2_global_obj_replace_unref(active, current);
-}
-
-static struct ast_sorcery_observer outbound_publish_observer = {
-	.loaded = prune_publish_clients,
-};
-
 static int load_module(void)
 {
+	CHECK_PJSIP_MODULE_LOADED();
+
+	ast_sorcery_apply_config(ast_sip_get_sorcery(), "res_pjsip_outbound_publish");
 	ast_sorcery_apply_default(ast_sip_get_sorcery(), "outbound-publish", "config", "pjsip.conf,criteria=type=outbound-publish");
 
 	if (ast_sorcery_object_register(ast_sip_get_sorcery(), "outbound-publish", sip_outbound_publish_alloc, NULL,
@@ -970,8 +1113,6 @@ static int load_module(void)
 		return AST_MODULE_LOAD_DECLINE;
 	}
 
-	ast_sorcery_observer_add(ast_sip_get_sorcery(), "outbound-publish", &outbound_publish_observer);
-
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "outbound-publish", "type", "", OPT_NOOP_T, 0, 0);
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "outbound-publish", "server_uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_outbound_publish, server_uri));
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "outbound-publish", "from_uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_outbound_publish, from_uri));
@@ -981,6 +1122,7 @@ static int load_module(void)
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "outbound-publish", "expiration", "3600", OPT_UINT_T, 0, FLDSET(struct ast_sip_outbound_publish, expiration));
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "outbound-publish", "max_auth_attempts", "5", OPT_UINT_T, 0, FLDSET(struct ast_sip_outbound_publish, max_auth_attempts));
 	ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "outbound-publish", "outbound_auth", "", outbound_auth_handler, NULL, NULL, 0, 0);
+
 	ast_sorcery_reload_object(ast_sip_get_sorcery(), "outbound-publish");
 
 	AST_RWLIST_RDLOCK(&publisher_handlers);
@@ -1004,7 +1146,44 @@ static int reload_module(void)
 
 static int unload_module(void)
 {
-	return 0;
+	struct timeval start = ast_tvnow();
+	struct timespec end = {
+		.tv_sec = start.tv_sec + 10,
+		.tv_nsec = start.tv_usec * 1000
+	};
+	int res = 0;
+	struct ao2_container *states = ao2_global_obj_ref(current_states);
+
+	if (!states || !(unloading.count = ao2_container_count(states))) {
+		return 0;
+	}
+	ao2_ref(states, -1);
+
+	ast_mutex_init(&unloading.lock);
+	ast_cond_init(&unloading.cond, NULL);
+	ast_mutex_lock(&unloading.lock);
+
+	unloading.is_unloading = 1;
+	ao2_global_obj_release(current_states);
+
+	/* wait for items to unpublish */
+	ast_verb(5, "Waiting to complete unpublishing task(s)\n");
+	while (unloading.count) {
+		res = ast_cond_timedwait(&unloading.cond, &unloading.lock, &end);
+	}
+	ast_mutex_unlock(&unloading.lock);
+
+	ast_mutex_destroy(&unloading.lock);
+	ast_cond_destroy(&unloading.cond);
+
+	if (res) {
+		ast_verb(5, "At least %d items were unable to unpublish "
+			"in the allowed time\n", unloading.count);
+	} else {
+		ast_verb(5, "All items successfully unpublished\n");
+	}
+
+	return res;
 }
 
 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "PJSIP Outbound Publish Support",
diff --git a/res/res_pjsip_outbound_registration.c b/res/res_pjsip_outbound_registration.c
index 552d61e..4ba5748 100644
--- a/res/res_pjsip_outbound_registration.c
+++ b/res/res_pjsip_outbound_registration.c
@@ -33,6 +33,9 @@
 #include "asterisk/taskprocessor.h"
 #include "asterisk/cli.h"
 #include "asterisk/stasis_system.h"
+#include "asterisk/threadstorage.h"
+#include "asterisk/threadpool.h"
+#include "asterisk/statsd.h"
 #include "res_pjsip/include/res_pjsip_private.h"
 
 /*** DOCUMENTATION
@@ -98,6 +101,21 @@
 						buggy registrars.
 					</para></description>
 				</configOption>
+				<configOption name="fatal_retry_interval" default="0">
+					<synopsis>Interval used when receiving a Fatal response.</synopsis>
+					<description><para>
+						If a fatal response is received, chan_pjsip will wait
+						<replaceable>fatal_retry_interval</replaceable> seconds before
+						attempting registration again. If 0 is specified, chan_pjsip will not
+						retry after receiving a fatal (non-temporary 4xx, 5xx, 6xx) response.
+						Setting this to a non-zero value may go against a "SHOULD NOT" in RFC3261,
+						but can be used to work around buggy registrars.</para>
+						<note><para>if also set the <replaceable>forbidden_retry_interval</replaceable>
+						takes precedence over this one when a 403 is received.
+						Also, if <replaceable>auth_rejection_permanent</replaceable> equals 'yes' then
+						a 401 and 407 become subject to this retry interval.</para></note>
+					</description>
+				</configOption>
 				<configOption name="server_uri">
 					<synopsis>SIP URI of the server to register against</synopsis>
 					<description><para>
@@ -114,6 +132,22 @@
 						<literal>pjsip.conf</literal>. As with other <literal>res_pjsip</literal> modules, this will use the first available transport of the appropriate type if unconfigured.</para></note>
 					</description>
 				</configOption>
+				<configOption name="line">
+					<synopsis>Whether to add a 'line' parameter to the Contact for inbound call matching</synopsis>
+					<description><para>
+						When enabled this option will cause a 'line' parameter to be added to the Contact
+						header placed into the outgoing registration request. If the remote server sends a call
+						this line parameter will be used to establish a relationship to the outbound registration,
+						ultimately causing the configured endpoint to be used.
+					</para></description>
+				</configOption>
+				<configOption name="endpoint">
+					<synopsis>Endpoint to use for incoming related calls</synopsis>
+					<description><para>
+						When line support is enabled this configured endpoint name is used for incoming calls
+						that are related to the outbound registration.
+					</para></description>
+				</configOption>
 				<configOption name="type">
 					<synopsis>Must be of type 'registration'.</synopsis>
 				</configOption>
@@ -140,11 +174,27 @@
 		</syntax>
 		<description>
 			<para>
-			Send a SIP REGISTER request to the specified outbound registration with an expiration of 0.
-			This will cause the contact added by this registration to be removed on the remote system.
-			Note: The specified outbound registration will attempt to re-register according to it's last
-			registration expiration.
-                        </para>
+			Unregisters the specified outbound registration and stops future registration attempts.
+			Call PJSIPRegister to start registration and schedule re-registrations according to configuration.
+            </para>
+		</description>
+	</manager>
+	<manager name="PJSIPRegister" language="en_US">
+		<synopsis>
+			Register an outbound registration.
+		</synopsis>
+		<syntax>
+			<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+			<parameter name="Registration" required="true">
+				<para>The outbound registration to register.</para>
+			</parameter>
+		</syntax>
+		<description>
+			<para>
+			Unregisters the specified outbound registration then starts registration and schedules re-registrations
+			according to configuration.
+			future registrations.
+            </para>
 		</description>
 	</manager>
 	<manager name="PJSIPShowRegistrationsOutbound" language="en_US">
@@ -163,6 +213,9 @@
 	</manager>
  ***/
 
+/*! \brief Some thread local storage used to determine if the running thread invoked the callback */
+AST_THREADSTORAGE(register_callback_invoked);
+
 /*! \brief Amount of buffer time (in seconds) before expiration that we re-register at */
 #define REREGISTER_BUFFER_TIME 10
 
@@ -179,17 +232,41 @@ enum sip_outbound_registration_status {
 	SIP_REGISTRATION_REJECTED_TEMPORARY,
 	/*! \brief Registration was rejected, permanently */
 	SIP_REGISTRATION_REJECTED_PERMANENT,
+	/*! \brief Registration is stopping. */
+	SIP_REGISTRATION_STOPPING,
 	/*! \brief Registration has been stopped */
 	SIP_REGISTRATION_STOPPED,
 };
 
-static const char *sip_outbound_registration_status_str[] = {
-	[SIP_REGISTRATION_UNREGISTERED] = "Unregistered",
-	[SIP_REGISTRATION_REGISTERED] = "Registered",
-	[SIP_REGISTRATION_REJECTED_TEMPORARY] = "Rejected",
-	[SIP_REGISTRATION_REJECTED_PERMANENT] = "Rejected",
-	[SIP_REGISTRATION_STOPPED] = "Stopped",
-};
+/*!
+ * \internal
+ * \brief Convert the internal registration state to an external status string.
+ * \since 13.5.0
+ *
+ * \param state Current outbound registration state.
+ *
+ * \return External registration status string.
+ */
+static const char *sip_outbound_registration_status_str(enum sip_outbound_registration_status state)
+{
+	const char *str;
+
+	str = "Unregistered";
+	switch (state) {
+	case SIP_REGISTRATION_STOPPING:
+	case SIP_REGISTRATION_STOPPED:
+	case SIP_REGISTRATION_UNREGISTERED:
+		break;
+	case SIP_REGISTRATION_REGISTERED:
+		str = "Registered";
+		break;
+	case SIP_REGISTRATION_REJECTED_TEMPORARY:
+	case SIP_REGISTRATION_REJECTED_PERMANENT:
+		str = "Rejected";
+		break;
+	}
+	return str;
+}
 
 /*! \brief Outbound registration information */
 struct sip_outbound_registration {
@@ -216,6 +293,8 @@ struct sip_outbound_registration {
 	unsigned int retry_interval;
 	/*! \brief Interval at which retries should occur for permanent responses */
 	unsigned int forbidden_retry_interval;
+	/*! \brief Interval at which retries should occur for all permanent responses */
+	unsigned int fatal_retry_interval;
 	/*! \brief Treat authentication challenges that we cannot handle as permanent failures */
 	unsigned int auth_rejection_permanent;
 	/*! \brief Maximum number of retries permitted */
@@ -230,9 +309,14 @@ struct sip_outbound_registration {
 
 /*! \brief Outbound registration client state information (persists for lifetime of regc) */
 struct sip_outbound_registration_client_state {
-	/*! \brief Current status of this registration */
+	/*! \brief Current state of this registration */
 	enum sip_outbound_registration_status status;
-	/*! \brief Outbound registration client */
+	/*!
+	 * \brief Outbound registration client
+	 * \note May only be accessed within the serializer thread
+	 * because it might get destroyed and set to NULL for
+	 * module unload.
+	 */
 	pjsip_regc *client;
 	/*! \brief Timer entry for retrying on temporal responses */
 	pj_timer_entry timer;
@@ -246,16 +330,22 @@ struct sip_outbound_registration_client_state {
 	unsigned int retry_interval;
 	/*! \brief Interval at which retries should occur for permanent responses */
 	unsigned int forbidden_retry_interval;
+	/*! \brief Interval at which retries should occur for all permanent responses */
+	unsigned int fatal_retry_interval;
 	/*! \brief Treat authentication challenges that we cannot handle as permanent failures */
 	unsigned int auth_rejection_permanent;
 	/*! \brief Determines whether SIP Path support should be advertised */
 	unsigned int support_path;
+	/*! CSeq number of last sent auth request. */
+	unsigned int auth_cseq;
 	/*! \brief Serializer for stuff and things */
 	struct ast_taskprocessor *serializer;
 	/*! \brief Configured authentication credentials */
 	struct ast_sip_auth_vector outbound_auths;
 	/*! \brief Registration should be destroyed after completion of transaction */
 	unsigned int destroy:1;
+	/*! \brief Non-zero if we have attempted sending a REGISTER with authentication */
+	unsigned int auth_attempted:1;
 };
 
 /*! \brief Outbound registration state information (persists for lifetime that registration should exist) */
@@ -266,6 +356,12 @@ struct sip_outbound_registration_state {
 	struct sip_outbound_registration_client_state *client_state;
 };
 
+/*! Time needs to be long enough for a transaction to timeout if nothing replies. */
+#define MAX_UNLOAD_TIMEOUT_TIME		35	/* Seconds */
+
+/*! Shutdown group to monitor sip_outbound_registration_client_state serializers. */
+static struct ast_serializer_shutdown_group *shutdown_group;
+
 /*! \brief Default number of state container buckets */
 #define DEFAULT_STATE_BUCKETS 53
 static AO2_GLOBAL_OBJ_STATIC(current_states);
@@ -322,45 +418,79 @@ static int registration_state_cmp(void *obj, void *arg, int flags)
 
 static struct sip_outbound_registration_state *get_state(const char *id)
 {
-	RAII_VAR(struct ao2_container *, states,
-		 ao2_global_obj_ref(current_states), ao2_cleanup);
-	return states ? ao2_find(states, id, OBJ_SEARCH_KEY) : NULL;
-}
-
-static int registration_state_add(void *obj, void *arg, int flags)
-{
-	struct sip_outbound_registration_state *state =
-		get_state(ast_sorcery_object_get_id(obj));
+	struct sip_outbound_registration_state *state = NULL;
+	struct ao2_container *states;
 
-	if (state) {
-		ao2_link(arg, state);
-		ao2_ref(state, -1);
+	states = ao2_global_obj_ref(current_states);
+	if (states) {
+		state = ao2_find(states, id, OBJ_SEARCH_KEY);
+		ao2_ref(states, -1);
 	}
-
-	return 0;
+	return state;
 }
 
 static struct ao2_container *get_registrations(void)
 {
-	RAII_VAR(struct ao2_container *, new_states, NULL, ao2_cleanup);
 	struct ao2_container *registrations = ast_sorcery_retrieve_by_fields(
 		ast_sip_get_sorcery(), "registration",
 		AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
 
-	if (!(new_states = ao2_container_alloc(DEFAULT_STATE_BUCKETS,
-		      registration_state_hash, registration_state_cmp))) {
-		ast_log(LOG_ERROR, "Unable to allocate registration states container\n");
+	return registrations;
+}
+
+/*! \brief Callback function for matching an outbound registration based on line */
+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;
+}
+
+static struct pjsip_param *get_uri_option_line(const void *uri)
+{
+	pjsip_sip_uri *pjuri;
+	static const pj_str_t LINE_STR = { "line", 4 };
+
+	if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri)) {
 		return NULL;
 	}
+	pjuri = pjsip_uri_get_uri(uri);
+	return pjsip_param_find(&pjuri->other_param, &LINE_STR);
+}
+
+/*! \brief Endpoint identifier which uses the 'line' parameter to establish a relationship to an outgoing registration */
+static struct ast_sip_endpoint *line_identify(pjsip_rx_data *rdata)
+{
+	pjsip_param *line;
+	RAII_VAR(struct ao2_container *, states, NULL, ao2_cleanup);
+	RAII_VAR(struct sip_outbound_registration_state *, state, NULL, ao2_cleanup);
 
-	if (registrations && ao2_container_count(registrations)) {
-		ao2_callback(registrations, OBJ_NODATA, registration_state_add, new_states);
+	if (!(line = get_uri_option_line(rdata->msg_info.to->uri))
+		&& !(line = get_uri_option_line(rdata->msg_info.msg->line.req.uri))) {
+		return NULL;
 	}
 
-	ao2_global_obj_replace_unref(current_states, new_states);
-	return registrations;
+	states = ao2_global_obj_ref(current_states);
+	if (!states) {
+		return NULL;
+	}
+
+	state = ao2_callback(states, 0, line_identify_relationship, line);
+	if (!state || ast_strlen_zero(state->registration->endpoint)) {
+		return NULL;
+	}
+
+	ast_debug(3, "Determined relationship to outbound registration '%s' based on line '%s', using configured endpoint '%s'\n",
+		ast_sorcery_object_get_id(state->registration), state->client_state->line, state->registration->endpoint);
+
+	return ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", state->registration->endpoint);
 }
 
+static struct ast_sip_endpoint_identifier line_identifier = {
+	.identify_endpoint = line_identify,
+};
+
 /*! \brief Helper function which cancels the timer on a client */
 static void cancel_registration(struct sip_outbound_registration_client_state *client_state)
 {
@@ -372,26 +502,52 @@ static void cancel_registration(struct sip_outbound_registration_client_state *c
 
 static pj_str_t PATH_NAME = { "path", 4 };
 
+/*! \brief Helper function which sends a message and cleans up, if needed, on failure */
+static pj_status_t registration_client_send(struct sip_outbound_registration_client_state *client_state,
+	pjsip_tx_data *tdata)
+{
+	pj_status_t status;
+	int *callback_invoked;
+
+	callback_invoked = ast_threadstorage_get(&register_callback_invoked, sizeof(int));
+	if (!callback_invoked) {
+		return PJ_ENOMEM;
+	}
+	*callback_invoked = 0;
+
+	/* Due to the message going out the callback may now be invoked, so bump the count */
+	ao2_ref(client_state, +1);
+	status = pjsip_regc_send(client_state->client, tdata);
+
+	/* If the attempt to send the message failed and the callback was not invoked we need to
+	 * drop the reference we just added
+	 */
+	if ((status != PJ_SUCCESS) && !(*callback_invoked)) {
+		ao2_ref(client_state, -1);
+	}
+
+	return status;
+}
+
 /*! \brief Callback function for registering */
 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], client_uri[PJSIP_MAX_URL_SIZE];
-
-	cancel_registration(client_state);
+	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)) {
+	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(3, "REGISTER attempt %u to '%s' with client '%s'\n",
-		  client_state->retries + 1, server_uri, client_uri);
+	ast_debug(1, "Outbound REGISTER attempt %u to '%s' with client '%s'\n",
+		client_state->retries + 1, server_uri, client_uri);
 
 	if (client_state->support_path) {
 		pjsip_supported_hdr *hdr;
@@ -411,11 +567,7 @@ static int handle_client_registration(void *data)
 		pj_strassign(&hdr->values[hdr->count++], &PATH_NAME);
 	}
 
-	/* Due to the registration the callback may now get called, so bump the ref count */
-	ao2_ref(client_state, +1);
-	if (pjsip_regc_send(client_state->client, tdata) != PJ_SUCCESS) {
-		ao2_ref(client_state, -1);
-	}
+	registration_client_send(client_state, tdata);
 
 	return 0;
 }
@@ -425,60 +577,110 @@ static void sip_outbound_registration_timer_cb(pj_timer_heap_t *timer_heap, stru
 {
 	struct sip_outbound_registration_client_state *client_state = entry->user_data;
 
-	ao2_ref(client_state, +1);
+	entry->id = 0;
+
+	/*
+	 * Transfer client_state reference to serializer task so the
+	 * nominal path will not dec the client_state ref in this
+	 * pjproject callback thread.
+	 */
 	if (ast_sip_push_task(client_state->serializer, handle_client_registration, client_state)) {
-		ast_log(LOG_WARNING, "Failed to pass outbound registration to threadpool\n");
+		ast_log(LOG_WARNING, "Scheduled outbound registration could not be executed.\n");
 		ao2_ref(client_state, -1);
 	}
-
-	entry->id = 0;
 }
 
 /*! \brief Helper function which sets up the timer to re-register in a specific amount of time */
 static void schedule_registration(struct sip_outbound_registration_client_state *client_state, unsigned int seconds)
 {
 	pj_time_val delay = { .sec = seconds, };
+	pjsip_regc_info info;
 
 	cancel_registration(client_state);
 
+	pjsip_regc_get_info(client_state->client, &info);
+	ast_debug(1, "Scheduling outbound registration to server '%.*s' from client '%.*s' in %d seconds\n",
+			(int) info.server_uri.slen, info.server_uri.ptr,
+			(int) info.client_uri.slen, info.client_uri.ptr,
+			seconds);
+
 	ao2_ref(client_state, +1);
 	if (pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(), &client_state->timer, &delay) != PJ_SUCCESS) {
-		ast_log(LOG_WARNING, "Failed to pass timed registration to scheduler\n");
+		ast_log(LOG_WARNING, "Failed to schedule registration to server '%.*s' from client '%.*s'\n",
+				(int) info.server_uri.slen, info.server_uri.ptr,
+				(int) info.client_uri.slen, info.client_uri.ptr);
 		ao2_ref(client_state, -1);
 	}
 }
 
+static void update_client_state_status(struct sip_outbound_registration_client_state *client_state, enum sip_outbound_registration_status status)
+{
+	if (client_state->status == status) {
+		return;
+	}
+
+	ast_statsd_log_string_va("PJSIP.registrations.state.%s", AST_STATSD_GAUGE, "-1", 1.0,
+		sip_outbound_registration_status_str(client_state->status));
+	ast_statsd_log_string_va("PJSIP.registrations.state.%s", AST_STATSD_GAUGE, "+1", 1.0,
+		sip_outbound_registration_status_str(status));
+	client_state->status = status;
+}
+
 /*! \brief Callback function for unregistering (potentially) and destroying state */
 static int handle_client_state_destruction(void *data)
 {
-	RAII_VAR(struct sip_outbound_registration_client_state *, client_state, data, ao2_cleanup);
+	struct sip_outbound_registration_client_state *client_state = data;
 
 	cancel_registration(client_state);
 
 	if (client_state->client) {
 		pjsip_regc_info info;
+		pjsip_tx_data *tdata;
 
 		pjsip_regc_get_info(client_state->client, &info);
 
 		if (info.is_busy == PJ_TRUE) {
 			/* If a client transaction is in progress we defer until it is complete */
+			ast_debug(1,
+				"Registration transaction is busy with server '%.*s' from client '%.*s'.\n",
+				(int) info.server_uri.slen, info.server_uri.ptr,
+				(int) info.client_uri.slen, info.client_uri.ptr);
 			client_state->destroy = 1;
+			ao2_ref(client_state, -1);
 			return 0;
 		}
 
-		if (client_state->status != SIP_REGISTRATION_UNREGISTERED && client_state->status != SIP_REGISTRATION_REJECTED_PERMANENT) {
-			pjsip_tx_data *tdata;
+		switch (client_state->status) {
+		case SIP_REGISTRATION_UNREGISTERED:
+			break;
+		case SIP_REGISTRATION_REGISTERED:
+			ast_debug(1,
+				"Trying to unregister with server '%.*s' from client '%.*s' before destruction.\n",
+				(int) info.server_uri.slen, info.server_uri.ptr,
+				(int) info.client_uri.slen, info.client_uri.ptr);
 
-			if (pjsip_regc_unregister(client_state->client, &tdata) == PJ_SUCCESS) {
-				pjsip_regc_send(client_state->client, tdata);
+			update_client_state_status(client_state, SIP_REGISTRATION_STOPPING);
+			client_state->destroy = 1;
+			if (pjsip_regc_unregister(client_state->client, &tdata) == PJ_SUCCESS
+				&& registration_client_send(client_state, tdata) == PJ_SUCCESS) {
+				ao2_ref(client_state, -1);
+				return 0;
 			}
+			break;
+		case SIP_REGISTRATION_REJECTED_TEMPORARY:
+		case SIP_REGISTRATION_REJECTED_PERMANENT:
+		case SIP_REGISTRATION_STOPPING:
+		case SIP_REGISTRATION_STOPPED:
+			break;
 		}
 
 		pjsip_regc_destroy(client_state->client);
+		client_state->client = NULL;
 	}
 
-	client_state->status = SIP_REGISTRATION_STOPPED;
+	update_client_state_status(client_state, SIP_REGISTRATION_STOPPED);
 	ast_sip_auth_vector_destroy(&client_state->outbound_auths);
+	ao2_ref(client_state, -1);
 
 	return 0;
 }
@@ -495,8 +697,8 @@ struct registration_response {
 	struct sip_outbound_registration_client_state *client_state;
 	/*! \brief The response message */
 	pjsip_rx_data *rdata;
-	/*! \brief The response transaction */
-	pjsip_transaction *tsx;
+	/*! \brief Request for which the response was received */
+	pjsip_tx_data *old_request;
 };
 
 /*! \brief Registration response structure destructor */
@@ -508,10 +710,14 @@ static void registration_response_destroy(void *obj)
 		pjsip_rx_data_free_cloned(response->rdata);
 	}
 
+	if (response->old_request) {
+		pjsip_tx_data_dec_ref(response->old_request);
+	}
+
 	ao2_cleanup(response->client_state);
 }
 
-/* \brief Helper funtion which determines if a response code is temporal or not */
+/*! \brief Helper function which determines if a response code is temporal or not */
 static int sip_outbound_registration_is_temporal(unsigned int code,
 		struct sip_outbound_registration_client_state *client_state)
 {
@@ -534,7 +740,7 @@ static int sip_outbound_registration_is_temporal(unsigned int code,
 static void schedule_retry(struct registration_response *response, unsigned int interval,
 			   const char *server_uri, const char *client_uri)
 {
-	response->client_state->status = SIP_REGISTRATION_REJECTED_TEMPORARY;
+	update_client_state_status(response->client_state, SIP_REGISTRATION_REJECTED_TEMPORARY);
 	schedule_registration(response->client_state, interval);
 
 	if (response->rdata) {
@@ -551,11 +757,13 @@ static void schedule_retry(struct registration_response *response, unsigned int
 /*! \brief Callback function for handling a response to a registration attempt */
 static int handle_registration_response(void *data)
 {
-	RAII_VAR(struct registration_response *, response, data, ao2_cleanup);
+	struct registration_response *response = data;
 	pjsip_regc_info info;
-	char server_uri[PJSIP_MAX_URL_SIZE], client_uri[PJSIP_MAX_URL_SIZE];
+	char server_uri[PJSIP_MAX_URL_SIZE];
+	char client_uri[PJSIP_MAX_URL_SIZE];
 
 	if (response->client_state->status == SIP_REGISTRATION_STOPPED) {
+		ao2_ref(response, -1);
 		return 0;
 	}
 
@@ -563,38 +771,71 @@ static int handle_registration_response(void *data)
 	ast_copy_pj_str(server_uri, &info.server_uri, sizeof(server_uri));
 	ast_copy_pj_str(client_uri, &info.client_uri, sizeof(client_uri));
 
-	if (response->code == 401 || response->code == 407) {
+	ast_debug(1, "Processing REGISTER response %d from server '%s' for client '%s'\n",
+			response->code, server_uri, client_uri);
+
+	if ((response->code == 401 || response->code == 407)
+		&& (!response->client_state->auth_attempted
+			|| response->rdata->msg_info.cseq->cseq != response->client_state->auth_cseq)) {
+		int res;
+		pjsip_cseq_hdr *cseq_hdr;
 		pjsip_tx_data *tdata;
-		if (!ast_sip_create_request_with_auth(&response->client_state->outbound_auths,
-				response->rdata, response->tsx, &tdata)) {
-			ao2_ref(response->client_state, +1);
-			if (pjsip_regc_send(response->client_state->client, tdata) != PJ_SUCCESS) {
-				ao2_cleanup(response->client_state);
+
+		if (!ast_sip_create_request_with_auth_from_old(&response->client_state->outbound_auths,
+				response->rdata, response->old_request, &tdata)) {
+			response->client_state->auth_attempted = 1;
+			ast_debug(1, "Sending authenticated REGISTER to server '%s' from client '%s'\n",
+					server_uri, client_uri);
+			pjsip_tx_data_add_ref(tdata);
+			res = registration_client_send(response->client_state, tdata);
+
+			/* Save the cseq that actually got sent. */
+			cseq_hdr = (pjsip_cseq_hdr *) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ,
+				NULL);
+			response->client_state->auth_cseq = cseq_hdr->cseq;
+			pjsip_tx_data_dec_ref(tdata);
+			if (res == PJ_SUCCESS) {
+				ao2_ref(response, -1);
+				return 0;
 			}
-			return 0;
+		} else {
+			ast_log(LOG_WARNING, "Failed to create authenticated REGISTER request to server '%s' from client '%s'\n",
+					server_uri, client_uri);
 		}
 		/* Otherwise, fall through so the failure is processed appropriately */
 	}
 
+	response->client_state->auth_attempted = 0;
+
 	if (PJSIP_IS_STATUS_IN_CLASS(response->code, 200)) {
 		/* Check if this is in regards to registering or unregistering */
 		if (response->expiration) {
+			int next_registration_round;
+
 			/* If the registration went fine simply reschedule registration for the future */
 			ast_debug(1, "Outbound registration to '%s' with client '%s' successful\n", server_uri, client_uri);
-			response->client_state->status = SIP_REGISTRATION_REGISTERED;
+			update_client_state_status(response->client_state, SIP_REGISTRATION_REGISTERED);
 			response->client_state->retries = 0;
-			schedule_registration(response->client_state, response->expiration - REREGISTER_BUFFER_TIME);
+			next_registration_round = response->expiration - REREGISTER_BUFFER_TIME;
+			if (next_registration_round < 0) {
+				/* Re-register immediately. */
+				next_registration_round = 0;
+			}
+			schedule_registration(response->client_state, next_registration_round);
 		} else {
 			ast_debug(1, "Outbound unregistration to '%s' with client '%s' successful\n", server_uri, client_uri);
-			response->client_state->status = SIP_REGISTRATION_UNREGISTERED;
+			update_client_state_status(response->client_state, SIP_REGISTRATION_UNREGISTERED);
 		}
+	} else if (response->client_state->destroy) {
+		/* We need to deal with the pending destruction instead. */
 	} else if (response->retry_after) {
 		/* If we have been instructed to retry after a period of time, schedule it as such */
 		schedule_retry(response, response->retry_after, server_uri, client_uri);
-	} else if (response->client_state->retry_interval && sip_outbound_registration_is_temporal(response->code, response->client_state)) {
+	} else if (response->client_state->retry_interval
+		&& sip_outbound_registration_is_temporal(response->code, response->client_state)) {
 		if (response->client_state->retries == response->client_state->max_retries) {
 			/* If we received enough temporal responses to exceed our maximum give up permanently */
-			response->client_state->status = SIP_REGISTRATION_REJECTED_PERMANENT;
+			update_client_state_status(response->client_state, SIP_REGISTRATION_REJECTED_PERMANENT);
 			ast_log(LOG_WARNING, "Maximum retries reached when attempting outbound registration to '%s' with client '%s', stopping registration attempt\n",
 				server_uri, client_uri);
 		} else {
@@ -607,14 +848,22 @@ static int handle_registration_response(void *data)
 			&& response->client_state->forbidden_retry_interval
 			&& response->client_state->retries < response->client_state->max_retries) {
 			/* A forbidden response retry interval is configured and there are retries remaining */
-			response->client_state->status = SIP_REGISTRATION_REJECTED_TEMPORARY;
+			update_client_state_status(response->client_state, SIP_REGISTRATION_REJECTED_TEMPORARY);
 			response->client_state->retries++;
 			schedule_registration(response->client_state, response->client_state->forbidden_retry_interval);
 			ast_log(LOG_WARNING, "403 Forbidden fatal response received from '%s' on registration attempt to '%s', retrying in '%u' seconds\n",
 				server_uri, client_uri, response->client_state->forbidden_retry_interval);
+		} else if (response->client_state->fatal_retry_interval
+			   && response->client_state->retries < response->client_state->max_retries) {
+			/* Some kind of fatal failure response received, so retry according to configured interval */
+			update_client_state_status(response->client_state, SIP_REGISTRATION_REJECTED_TEMPORARY);
+			response->client_state->retries++;
+			schedule_registration(response->client_state, response->client_state->fatal_retry_interval);
+			ast_log(LOG_WARNING, "'%d' fatal response received from '%s' on registration attempt to '%s', retrying in '%u' seconds\n",
+				response->code, server_uri, client_uri, response->client_state->fatal_retry_interval);
 		} else {
 			/* Finally if there's no hope of registering give up */
-			response->client_state->status = SIP_REGISTRATION_REJECTED_PERMANENT;
+			update_client_state_status(response->client_state, SIP_REGISTRATION_REJECTED_PERMANENT);
 			if (response->rdata) {
 				ast_log(LOG_WARNING, "Fatal response '%d' received from '%s' on registration attempt to '%s', stopping outbound registration\n",
 					response->code, server_uri, client_uri);
@@ -624,35 +873,68 @@ static int handle_registration_response(void *data)
 		}
 	}
 
-	ast_system_publish_registry("PJSIP", client_uri, server_uri, sip_outbound_registration_status_str[response->client_state->status], NULL);
+	ast_system_publish_registry("PJSIP", client_uri, server_uri,
+		sip_outbound_registration_status_str(response->client_state->status), NULL);
 
-	/* If deferred destruction is in use see if we need to destroy now */
 	if (response->client_state->destroy) {
+		/* We have a pending deferred destruction to complete now. */
+		ao2_ref(response->client_state, +1);
 		handle_client_state_destruction(response->client_state);
 	}
 
+	ao2_ref(response, -1);
 	return 0;
 }
 
 /*! \brief Callback function for outbound registration client */
 static void sip_outbound_registration_response_cb(struct pjsip_regc_cbparam *param)
 {
-	RAII_VAR(struct sip_outbound_registration_client_state *, client_state, param->token, ao2_cleanup);
-	struct registration_response *response = ao2_alloc(sizeof(*response), registration_response_destroy);
+	struct sip_outbound_registration_client_state *client_state = param->token;
+	struct registration_response *response;
+	int *callback_invoked;
+
+	callback_invoked = ast_threadstorage_get(&register_callback_invoked, sizeof(int));
+
+	ast_assert(callback_invoked != NULL);
+	ast_assert(client_state != NULL);
+
+	*callback_invoked = 1;
 
+	response = ao2_alloc(sizeof(*response), registration_response_destroy);
+	if (!response) {
+		ao2_ref(client_state, -1);
+		return;
+	}
 	response->code = param->code;
 	response->expiration = param->expiration;
+	/*
+	 * Transfer client_state reference to response so the
+	 * nominal path will not dec the client_state ref in this
+	 * pjproject callback thread.
+	 */
 	response->client_state = client_state;
-	ao2_ref(response->client_state, +1);
+
+	ast_debug(1, "Received REGISTER response %d(%.*s)\n",
+		param->code, (int) param->reason.slen, param->reason.ptr);
 
 	if (param->rdata) {
-		struct pjsip_retry_after_hdr *retry_after = pjsip_msg_find_hdr(param->rdata->msg_info.msg, PJSIP_H_RETRY_AFTER, NULL);
+		struct pjsip_retry_after_hdr *retry_after;
+		pjsip_transaction *tsx;
 
+		retry_after = pjsip_msg_find_hdr(param->rdata->msg_info.msg, PJSIP_H_RETRY_AFTER,
+			NULL);
 		response->retry_after = retry_after ? retry_after->ivalue : 0;
-		response->tsx = pjsip_rdata_get_tsx(param->rdata);
+		tsx = pjsip_rdata_get_tsx(param->rdata);
+		response->old_request = tsx->last_tx;
+		pjsip_tx_data_add_ref(response->old_request);
 		pjsip_rx_data_clone(param->rdata, 0, &response->rdata);
 	}
 
+	/*
+	 * Transfer response reference to serializer task so the
+	 * nominal path will not dec the response ref in this
+	 * pjproject callback thread.
+	 */
 	if (ast_sip_push_task(client_state->serializer, handle_registration_response, response)) {
 		ast_log(LOG_WARNING, "Failed to pass incoming registration response to threadpool\n");
 		ao2_cleanup(response);
@@ -664,13 +946,16 @@ 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);
 	ao2_cleanup(state->registration);
 
 	if (!state->client_state) {
-		return;
-	}
-
-	if (state->client_state->serializer && ast_sip_push_task(state->client_state->serializer, handle_client_state_destruction, state->client_state)) {
+		/* Nothing to do */
+	} else if (!state->client_state->serializer) {
+		ao2_ref(state->client_state, -1);
+	} else if (ast_sip_push_task(state->client_state->serializer,
+		handle_client_state_destruction, state->client_state)) {
 		ast_log(LOG_WARNING, "Failed to pass outbound registration client destruction to threadpool\n");
 		ao2_ref(state->client_state, -1);
 	}
@@ -681,29 +966,42 @@ static void sip_outbound_registration_client_state_destroy(void *obj)
 {
 	struct sip_outbound_registration_client_state *client_state = obj;
 
+	ast_statsd_log_string("PJSIP.registrations.count", AST_STATSD_GAUGE, "-1", 1.0);
+	ast_statsd_log_string_va("PJSIP.registrations.state.%s", AST_STATSD_GAUGE, "-1", 1.0,
+		sip_outbound_registration_status_str(client_state->status));
+
 	ast_taskprocessor_unreference(client_state->serializer);
 }
 
 /*! \brief Allocator function for registration state */
 static struct sip_outbound_registration_state *sip_outbound_registration_state_alloc(struct sip_outbound_registration *registration)
 {
-	struct sip_outbound_registration_state *state = ao2_alloc(sizeof(*state), sip_outbound_registration_state_destroy);
+	struct sip_outbound_registration_state *state;
 
-	if (!state || !(state->client_state = ao2_alloc(sizeof(*state->client_state), sip_outbound_registration_client_state_destroy))) {
+	state = ao2_alloc(sizeof(*state), sip_outbound_registration_state_destroy);
+	if (!state) {
+		return NULL;
+	}
+	state->client_state = ao2_alloc(sizeof(*state->client_state),
+		sip_outbound_registration_client_state_destroy);
+	if (!state->client_state) {
 		ao2_cleanup(state);
 		return NULL;
 	}
 
-	if (!(state->client_state->serializer = ast_sip_create_serializer())) {
-		ao2_cleanup(state->client_state);
+	state->client_state->serializer = ast_sip_create_serializer_group(shutdown_group);
+	if (!state->client_state->serializer) {
 		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;
 
+	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;
 }
@@ -721,7 +1019,10 @@ static void sip_outbound_registration_destroy(void *obj)
 /*! \brief Allocator function for registration information */
 static void *sip_outbound_registration_alloc(const char *name)
 {
-	struct sip_outbound_registration *registration = ast_sorcery_generic_alloc(sizeof(*registration), sip_outbound_registration_destroy);
+	struct sip_outbound_registration *registration;
+
+	registration = ast_sorcery_generic_alloc(sizeof(*registration),
+		sip_outbound_registration_destroy);
 	if (!registration || ast_string_field_init(registration, 256)) {
 		ao2_cleanup(registration);
 		return NULL;
@@ -731,7 +1032,8 @@ static void *sip_outbound_registration_alloc(const char *name)
 }
 
 /*! \brief Helper function which populates a pj_str_t with a contact header */
-static int sip_dialog_create_contact(pj_pool_t *pool, pj_str_t *contact, const char *user, const pj_str_t *target, pjsip_tpselector *selector)
+static int sip_dialog_create_contact(pj_pool_t *pool, pj_str_t *contact, const char *user,
+	const pj_str_t *target, pjsip_tpselector *selector, const char *line)
 {
 	pj_str_t tmp, local_addr;
 	pjsip_uri *uri;
@@ -764,8 +1066,8 @@ static int sip_dialog_create_contact(pj_pool_t *pool, pj_str_t *contact, const c
 		type = (pjsip_transport_type_e)(((int)type) + PJSIP_TRANSPORT_IPV6);
 	}
 
-	if (pjsip_tpmgr_find_local_addr(pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()), pool, type, selector,
-							      &local_addr, &local_port) != PJ_SUCCESS) {
+	if (pjsip_tpmgr_find_local_addr(pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()),
+		pool, type, selector, &local_addr, &local_port) != PJ_SUCCESS) {
 		return -1;
 	}
 
@@ -775,16 +1077,18 @@ static int sip_dialog_create_contact(pj_pool_t *pool, pj_str_t *contact, const c
 
 	contact->ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
 	contact->slen = pj_ansi_snprintf(contact->ptr, PJSIP_MAX_URL_SIZE,
-				      "<%s:%s@%s%.*s%s:%d%s%s>",
-				      (pjsip_transport_get_flag_from_type(type) & PJSIP_TRANSPORT_SECURE) ? "sips" : "sip",
-				      user,
-				      (type & PJSIP_TRANSPORT_IPV6) ? "[" : "",
-				      (int)local_addr.slen,
-				      local_addr.ptr,
-				      (type & PJSIP_TRANSPORT_IPV6) ? "]" : "",
-				      local_port,
-				      (type != PJSIP_TRANSPORT_UDP && type != PJSIP_TRANSPORT_UDP6) ? ";transport=" : "",
-				      (type != PJSIP_TRANSPORT_UDP && type != PJSIP_TRANSPORT_UDP6) ? pjsip_transport_get_type_name(type) : "");
+		"<%s:%s@%s%.*s%s:%d%s%s%s%s>",
+		(pjsip_transport_get_flag_from_type(type) & PJSIP_TRANSPORT_SECURE) ? "sips" : "sip",
+		user,
+		(type & PJSIP_TRANSPORT_IPV6) ? "[" : "",
+		(int)local_addr.slen,
+		local_addr.ptr,
+		(type & PJSIP_TRANSPORT_IPV6) ? "]" : "",
+		local_port,
+		(type != PJSIP_TRANSPORT_UDP && type != PJSIP_TRANSPORT_UDP6) ? ";transport=" : "",
+		(type != PJSIP_TRANSPORT_UDP && type != PJSIP_TRANSPORT_UDP6) ? pjsip_transport_get_type_name(type) : "",
+		!ast_strlen_zero(line) ? ";line=" : "",
+		S_OR(line, ""));
 
 	return 0;
 }
@@ -799,25 +1103,27 @@ static int sip_dialog_create_contact(pj_pool_t *pool, pj_str_t *contact, const c
  * \param existing The pre-existing outbound registration
  * \param applied The newly-created registration
  */
-static int can_reuse_registration(struct sip_outbound_registration *existing, struct sip_outbound_registration *applied)
+static int can_reuse_registration(struct sip_outbound_registration *existing,
+	struct sip_outbound_registration *applied)
 {
-	int i;
-
-	if (strcmp(existing->server_uri, applied->server_uri) || strcmp(existing->client_uri, applied->client_uri) ||
-		strcmp(existing->transport, applied->transport) || strcmp(existing->contact_user, applied->contact_user) ||
-		strcmp(existing->outbound_proxy, applied->outbound_proxy) ||
-		AST_VECTOR_SIZE(&existing->outbound_auths) != AST_VECTOR_SIZE(&applied->outbound_auths) ||
-		existing->auth_rejection_permanent != applied->auth_rejection_permanent) {
-		return 0;
+	int rc = 1;
+	struct ast_sorcery *sorcery = ast_sip_get_sorcery();
+	struct ast_variable *ve = ast_sorcery_objectset_create(sorcery, existing);
+	struct ast_variable *va = ast_sorcery_objectset_create(sorcery, applied);
+	struct ast_variable *vc = NULL;
+
+	if (ast_sorcery_changeset_create(ve, va, &vc) || vc != NULL) {
+		rc = 0;
+		ast_debug(4, "Registration '%s' changed.  Can't re-use.\n", ast_sorcery_object_get_id(existing));
+	} else {
+		ast_debug(4, "Registration '%s' didn't change.  Can re-use\n", ast_sorcery_object_get_id(existing));
 	}
 
-	for (i = 0; i < AST_VECTOR_SIZE(&existing->outbound_auths); ++i) {
-		if (strcmp(AST_VECTOR_GET(&existing->outbound_auths, i), AST_VECTOR_GET(&applied->outbound_auths, i))) {
-			return 0;
-		}
-	}
+	ast_variables_destroy(ve);
+	ast_variables_destroy(va);
+	ast_variables_destroy(vc);
 
-	return 1;
+	return rc;
 }
 
 /*! \brief Helper function that allocates a pjsip registration client and configures it */
@@ -879,9 +1185,10 @@ static int sip_outbound_registration_regc_alloc(void *data)
 		}
 	}
 
-	if (!state->client_state->client &&
-		pjsip_regc_create(ast_sip_get_pjsip_endpoint(), state->client_state, sip_outbound_registration_response_cb,
-		&state->client_state->client) != PJ_SUCCESS) {
+	ast_assert(state->client_state->client == NULL);
+	if (pjsip_regc_create(ast_sip_get_pjsip_endpoint(), state->client_state,
+			sip_outbound_registration_response_cb,
+			&state->client_state->client) != PJ_SUCCESS) {
 		return -1;
 	}
 
@@ -894,8 +1201,11 @@ static int sip_outbound_registration_regc_alloc(void *data)
 
 		pj_list_init(&route_set);
 
-		pj_strdup2_with_null(pjsip_regc_get_pool(state->client_state->client), &tmp, registration->outbound_proxy);
-		if (!(route = pjsip_parse_hdr(pjsip_regc_get_pool(state->client_state->client), &ROUTE_HNAME, tmp.ptr, tmp.slen, NULL))) {
+		pj_strdup2_with_null(pjsip_regc_get_pool(state->client_state->client), &tmp,
+			registration->outbound_proxy);
+		route = pjsip_parse_hdr(pjsip_regc_get_pool(state->client_state->client),
+			&ROUTE_HNAME, tmp.ptr, tmp.slen, NULL);
+		if (!route) {
 			return -1;
 		}
 		pj_list_insert_nodes_before(&route_set, route);
@@ -903,14 +1213,22 @@ static int sip_outbound_registration_regc_alloc(void *data)
 		pjsip_regc_set_route_set(state->client_state->client, &route_set);
 	}
 
+	if (state->registration->line) {
+		ast_generate_random_string(state->client_state->line, sizeof(state->client_state->line));
+	}
+
 	pj_cstr(&server_uri, registration->server_uri);
 
-	if (sip_dialog_create_contact(pjsip_regc_get_pool(state->client_state->client), &contact_uri, S_OR(registration->contact_user, "s"), &server_uri, &selector)) {
+
+	if (sip_dialog_create_contact(pjsip_regc_get_pool(state->client_state->client),
+		&contact_uri, S_OR(registration->contact_user, "s"), &server_uri, &selector,
+		state->client_state->line)) {
 		return -1;
 	}
 
 	pj_cstr(&client_uri, registration->client_uri);
-	if (pjsip_regc_init(state->client_state->client, &server_uri, &client_uri, &client_uri, 1, &contact_uri, registration->expiration) != PJ_SUCCESS) {
+	if (pjsip_regc_init(state->client_state->client, &server_uri, &client_uri,
+		&client_uri, 1, &contact_uri, registration->expiration) != PJ_SUCCESS) {
 		return -1;
 	}
 
@@ -920,8 +1238,8 @@ static int sip_outbound_registration_regc_alloc(void *data)
 /*! \brief Helper function which performs a single registration */
 static int sip_outbound_registration_perform(void *data)
 {
-	RAII_VAR(struct sip_outbound_registration_state *, state, data, ao2_cleanup);
-	RAII_VAR(struct sip_outbound_registration *, registration, ao2_bump(state->registration), ao2_cleanup);
+	struct sip_outbound_registration_state *state = data;
+	struct sip_outbound_registration *registration = ao2_bump(state->registration);
 	size_t i;
 
 	/* Just in case the client state is being reused for this registration, free the auth information */
@@ -930,10 +1248,14 @@ static int sip_outbound_registration_perform(void *data)
 	AST_VECTOR_INIT(&state->client_state->outbound_auths, AST_VECTOR_SIZE(&registration->outbound_auths));
 	for (i = 0; i < AST_VECTOR_SIZE(&registration->outbound_auths); ++i) {
 		const char *name = ast_strdup(AST_VECTOR_GET(&registration->outbound_auths, i));
-		AST_VECTOR_APPEND(&state->client_state->outbound_auths, name);
+
+		if (name) {
+			AST_VECTOR_APPEND(&state->client_state->outbound_auths, name);
+		}
 	}
 	state->client_state->retry_interval = registration->retry_interval;
 	state->client_state->forbidden_retry_interval = registration->forbidden_retry_interval;
+	state->client_state->fatal_retry_interval = registration->fatal_retry_interval;
 	state->client_state->max_retries = registration->max_retries;
 	state->client_state->retries = 0;
 	state->client_state->support_path = registration->support_path;
@@ -943,6 +1265,8 @@ static int sip_outbound_registration_perform(void *data)
 
 	schedule_registration(state->client_state, (ast_random() % 10) + 1);
 
+	ao2_ref(registration, -1);
+	ao2_ref(state, -1);
 	return 0;
 }
 
@@ -950,23 +1274,49 @@ static int sip_outbound_registration_perform(void *data)
 static int sip_outbound_registration_apply(const struct ast_sorcery *sorcery, void *obj)
 {
 	RAII_VAR(struct ao2_container *, states, ao2_global_obj_ref(current_states), ao2_cleanup);
-	RAII_VAR(struct sip_outbound_registration_state *, state,
-		 ao2_find(states, ast_sorcery_object_get_id(obj), OBJ_SEARCH_KEY), ao2_cleanup);
+	RAII_VAR(struct sip_outbound_registration_state *, state, NULL, ao2_cleanup);
 	RAII_VAR(struct sip_outbound_registration_state *, new_state, NULL, ao2_cleanup);
 	struct sip_outbound_registration *applied = obj;
 
+	if (!states) {
+		/* Global container has gone.  Likely shutting down. */
+		return -1;
+	}
+	state = ao2_find(states, ast_sorcery_object_get_id(applied), OBJ_SEARCH_KEY);
+
+	ast_debug(4, "Applying configuration to outbound registration '%s'\n", ast_sorcery_object_get_id(applied));
+
 	if (ast_strlen_zero(applied->server_uri)) {
-		ast_log(LOG_ERROR, "No server URI specified on outbound registration '%s'",
+		ast_log(LOG_ERROR, "No server URI specified on outbound registration '%s'\n",
 			ast_sorcery_object_get_id(applied));
 		return -1;
 	} else if (ast_strlen_zero(applied->client_uri)) {
 		ast_log(LOG_ERROR, "No client URI specified on outbound registration '%s'\n",
 			ast_sorcery_object_get_id(applied));
 		return -1;
+	} else if (applied->line && ast_strlen_zero(applied->endpoint)) {
+		ast_log(LOG_ERROR, "Line support has been enabled on outbound registration '%s' without providing an endpoint\n",
+			ast_sorcery_object_get_id(applied));
+		return -1;
+	} else if (!ast_strlen_zero(applied->endpoint) && !applied->line) {
+		ast_log(LOG_ERROR, "An endpoint has been specified on outbound registration '%s' without enabling line support\n",
+			ast_sorcery_object_get_id(applied));
+		return -1;
 	}
 
 	if (state && can_reuse_registration(state->registration, applied)) {
+		ast_debug(4,
+			"No change between old configuration and new configuration on outbound registration '%s'. Using previous state\n",
+			ast_sorcery_object_get_id(applied));
+
+		/*
+		 * This is OK to replace without relinking the state in the
+		 * current_states container since state->registration and
+		 * applied have the same key.
+		 */
+		ao2_lock(states);
 		ao2_replace(state->registration, applied);
+		ao2_unlock(states);
 		return 0;
 	}
 
@@ -974,7 +1324,8 @@ static int sip_outbound_registration_apply(const struct ast_sorcery *sorcery, vo
 		return -1;
 	}
 
-	if (ast_sip_push_task_synchronous(NULL, sip_outbound_registration_regc_alloc, new_state)) {
+	if (ast_sip_push_task_synchronous(new_state->client_state->serializer,
+		sip_outbound_registration_regc_alloc, new_state)) {
 		return -1;
 	}
 
@@ -987,11 +1338,9 @@ static int sip_outbound_registration_apply(const struct ast_sorcery *sorcery, vo
 	}
 
 	ao2_lock(states);
-
 	if (state) {
 		ao2_unlink(states, state);
 	}
-
 	ao2_link(states, new_state);
 	ao2_unlock(states);
 
@@ -1032,19 +1381,22 @@ static int outbound_auths_to_var_list(const void *obj, struct ast_variable **fie
 
 static int unregister_task(void *obj)
 {
-	RAII_VAR(struct sip_outbound_registration_state*, state, obj, ao2_cleanup);
+	struct sip_outbound_registration_state *state = obj;
 	struct pjsip_regc *client = state->client_state->client;
 	pjsip_tx_data *tdata;
+	pjsip_regc_info info;
 
-	if (pjsip_regc_unregister(client, &tdata) != PJ_SUCCESS) {
-		return 0;
-	}
+	pjsip_regc_get_info(client, &info);
+	ast_debug(1, "Unregistering contacts with server '%s' from client '%s'\n",
+		state->registration->server_uri, state->registration->client_uri);
+
+	cancel_registration(state->client_state);
 
-	ao2_ref(state->client_state, +1);
-	if (pjsip_regc_send(client, tdata) != PJ_SUCCESS) {
-		ao2_cleanup(state->client_state);
+	if (pjsip_regc_unregister(client, &tdata) == PJ_SUCCESS) {
+		registration_client_send(state->client_state, tdata);
 	}
 
+	ao2_ref(state, -1);
 	return 0;
 }
 
@@ -1055,6 +1407,18 @@ static int queue_unregister(struct sip_outbound_registration_state *state)
 		ao2_ref(state, -1);
 		return -1;
 	}
+
+	return 0;
+}
+
+static int queue_register(struct sip_outbound_registration_state *state)
+{
+	ao2_ref(state, +1);
+	if (ast_sip_push_task(state->client_state->serializer, sip_outbound_registration_perform, state)) {
+		ao2_ref(state, -1);
+		return -1;
+	}
+
 	return 0;
 }
 
@@ -1065,7 +1429,7 @@ static char *cli_complete_registration(const char *line, const char *word,
 	int wordlen;
 	int which = 0;
 	struct sip_outbound_registration *registration;
-	RAII_VAR(struct ao2_container *, registrations, NULL, ao2_cleanup);
+	struct ao2_container *registrations;
 	struct ao2_iterator i;
 
 	if (pos != 3) {
@@ -1082,22 +1446,25 @@ static char *cli_complete_registration(const char *line, const char *word,
 	i = ao2_iterator_init(registrations, 0);
 	while ((registration = ao2_iterator_next(&i))) {
 		const char *name = ast_sorcery_object_get_id(registration);
+
 		if (!strncasecmp(word, name, wordlen) && ++which > state) {
 			result = ast_strdup(name);
 		}
 
-		ao2_cleanup(registration);
+		ao2_ref(registration, -1);
 		if (result) {
 			break;
 		}
 	}
 	ao2_iterator_destroy(&i);
+
+	ao2_ref(registrations, -1);
 	return result;
 }
 
 static char *cli_unregister(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-	RAII_VAR(struct sip_outbound_registration_state *, state, NULL, ao2_cleanup);
+	struct sip_outbound_registration_state *state;
 	const char *registration_name;
 
 	switch (cmd) {
@@ -1105,11 +1472,7 @@ static char *cli_unregister(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
 		e->command = "pjsip send unregister";
 		e->usage =
 			"Usage: pjsip send unregister <registration>\n"
-			"       Send a SIP REGISTER request to the specified outbound "
-			"registration with an expiration of 0. This will cause the contact "
-			"added by this registration to be removed on the remote system. Note: "
-			"The specified outbound registration will attempt to re-register "
-			"according to its last registration expiration.\n";
+			"       Unregisters the specified outbound registration and stops future registration attempts.\n";
 		return NULL;
 	case CLI_GENERATE:
 		return cli_complete_registration(a->line, a->word, a->pos, a->n);
@@ -1128,17 +1491,59 @@ static char *cli_unregister(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
 	}
 
 	if (queue_unregister(state)) {
-		ast_cli(a->fd, "Failed to queue unregistration");
-		return 0;
+		ast_cli(a->fd, "Failed to queue unregistration\n");
 	}
 
+	ao2_ref(state, -1);
+	return CLI_SUCCESS;
+}
+
+static char *cli_register(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	struct sip_outbound_registration_state *state;
+	const char *registration_name;
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "pjsip send register";
+		e->usage =
+			"Usage: pjsip send register <registration>\n"
+			"       Unregisters the specified outbound "
+			"registration then re-registers and re-schedules it.\n";
+		return NULL;
+	case CLI_GENERATE:
+		return cli_complete_registration(a->line, a->word, a->pos, a->n);
+	}
+
+	if (a->argc != 4) {
+		return CLI_SHOWUSAGE;
+	}
+
+	registration_name = a->argv[3];
+
+	state = get_state(registration_name);
+	if (!state) {
+		ast_cli(a->fd, "Unable to retrieve registration %s\n", registration_name);
+		return CLI_FAILURE;
+	}
+
+	/* We need to serialize the unregister and register so they need
+	 * to be queued as separate tasks.
+	 */
+	if (queue_unregister(state)) {
+		ast_cli(a->fd, "Failed to queue unregistration\n");
+	} else if (queue_register(state)) {
+		ast_cli(a->fd, "Failed to queue registration\n");
+	}
+
+	ao2_ref(state, -1);
 	return CLI_SUCCESS;
 }
 
 static int ami_unregister(struct mansession *s, const struct message *m)
 {
 	const char *registration_name = astman_get_header(m, "Registration");
-	RAII_VAR(struct sip_outbound_registration_state *, state, NULL, ao2_cleanup);
+	struct sip_outbound_registration_state *state;
 
 	if (ast_strlen_zero(registration_name)) {
 		astman_send_error(s, m, "Registration parameter missing.");
@@ -1153,10 +1558,42 @@ static int ami_unregister(struct mansession *s, const struct message *m)
 
 	if (queue_unregister(state)) {
 		astman_send_ack(s, m, "Failed to queue unregistration");
+	} else {
+		astman_send_ack(s, m, "Unregistration sent");
+	}
+
+	ao2_ref(state, -1);
+	return 0;
+}
+
+static int ami_register(struct mansession *s, const struct message *m)
+{
+	const char *registration_name = astman_get_header(m, "Registration");
+	struct sip_outbound_registration_state *state;
+
+	if (ast_strlen_zero(registration_name)) {
+		astman_send_error(s, m, "Registration parameter missing.");
 		return 0;
 	}
 
-	astman_send_ack(s, m, "Unregistration sent");
+	state = get_state(registration_name);
+	if (!state) {
+		astman_send_error(s, m, "Unable to retrieve registration entry\n");
+		return 0;
+	}
+
+	/* We need to serialize the unregister and register so they need
+	 * to be queued as separate tasks.
+	 */
+	if (queue_unregister(state)) {
+		astman_send_ack(s, m, "Failed to queue unregistration");
+	} else if (queue_register(state)) {
+		astman_send_ack(s, m, "Failed to queue unregistration");
+	} else {
+		astman_send_ack(s, m, "Reregistration sent");
+	}
+
+	ao2_ref(state, -1);
 	return 0;
 }
 
@@ -1170,10 +1607,10 @@ struct sip_ami_outbound {
 static int ami_outbound_registration_task(void *obj)
 {
 	struct sip_ami_outbound *ami = obj;
-	RAII_VAR(struct ast_str *, buf,
-		 ast_sip_create_ami_event("OutboundRegistrationDetail", ami->ami), ast_free);
+	struct ast_str *buf;
 	struct sip_outbound_registration_state *state;
 
+	buf = ast_sip_create_ami_event("OutboundRegistrationDetail", ami->ami);
 	if (!buf) {
 		return -1;
 	}
@@ -1182,22 +1619,24 @@ static int ami_outbound_registration_task(void *obj)
 
 	if ((state = get_state(ast_sorcery_object_get_id(ami->registration)))) {
 		pjsip_regc_info info;
+
 		if (state->client_state->status == SIP_REGISTRATION_REGISTERED) {
 			++ami->registered;
 		} else {
 			++ami->not_registered;
 		}
 
-		ast_str_append(&buf, 0, "Status: %s%s",
-			       sip_outbound_registration_status_str[
-				       state->client_state->status], "\r\n");
+		ast_str_append(&buf, 0, "Status: %s\r\n",
+			sip_outbound_registration_status_str(state->client_state->status));
 
 		pjsip_regc_get_info(state->client_state->client, &info);
-		ast_str_append(&buf, 0, "NextReg: %d%s", info.next_reg, "\r\n");
+		ast_str_append(&buf, 0, "NextReg: %d\r\n", info.next_reg);
 		ao2_ref(state, -1);
 	}
 
 	astman_append(ami->ami->s, "%s\r\n", ast_str_buffer(buf));
+	ast_free(buf);
+
 	return ast_sip_format_auths_ami(&ami->registration->outbound_auths, ami->ami);
 }
 
@@ -1215,36 +1654,39 @@ static int ami_show_outbound_registrations(struct mansession *s,
 {
 	struct ast_sip_ami ami = { .s = s, .m = m, .action_id = astman_get_header(m, "ActionID"), };
 	struct sip_ami_outbound ami_outbound = { .ami = &ami };
-	RAII_VAR(struct ao2_container *, regs, get_registrations(), ao2_cleanup);
+	struct ao2_container *regs;
 
+	regs = get_registrations();
 	if (!regs) {
-		astman_send_error(s, m, "Unable to retreive "
+		astman_send_error(s, m, "Unable to retrieve "
 				  "outbound registrations\n");
 		return -1;
 	}
 
-	astman_send_listack(s, m, "Following are Events for each Outbound "
-			    "registration", "start");
+	astman_send_listack(s, m, "Following are Events for each Outbound registration",
+		"start");
 
 	ao2_callback(regs, OBJ_NODATA, ami_outbound_registration_detail, &ami_outbound);
 
-	astman_append(s, "Event: OutboundRegistrationDetailComplete\r\n");
-	if (!ast_strlen_zero(ami.action_id)) {
-		astman_append(s, "ActionID: %s\r\n", ami.action_id);
-	}
-	astman_append(s, "EventList: Complete\r\n"
-		      "Registered: %d\r\n"
-		      "NotRegistered: %d\r\n\r\n",
-		      ami_outbound.registered,
-		      ami_outbound.not_registered);
+	astman_send_list_complete_start(s, m, "OutboundRegistrationDetailComplete",
+		ami_outbound.registered + ami_outbound.not_registered);
+	astman_append(s,
+		"Registered: %d\r\n"
+		"NotRegistered: %d\r\n",
+		ami_outbound.registered,
+		ami_outbound.not_registered);
+	astman_send_list_complete_end(s);
+
+	ao2_ref(regs, -1);
 	return 0;
 }
 
-static struct ao2_container *cli_get_container(void)
+static struct ao2_container *cli_get_container(const char *regex)
 {
-	RAII_VAR(struct ao2_container *, container, get_registrations(), ao2_cleanup);
+	RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup);
 	struct ao2_container *s_container;
 
+	container = ast_sorcery_retrieve_by_regex(ast_sip_get_sorcery(), "registration", regex);
 	if (!container) {
 		return NULL;
 	}
@@ -1277,9 +1719,11 @@ static void *cli_retrieve_by_id(const char *id)
 
 	if (!obj) {
 		/* if the object no longer exists then remove its state  */
-		ao2_find((states = ao2_global_obj_ref(current_states)),
-			 id, OBJ_SEARCH_KEY | OBJ_UNLINK | OBJ_NODATA);
-		ao2_ref(states, -1);
+		states = ao2_global_obj_ref(current_states);
+		if (states) {
+			ao2_find(states, id, OBJ_SEARCH_KEY | OBJ_UNLINK | OBJ_NODATA);
+			ao2_ref(states, -1);
+		}
 	}
 
 	return obj;
@@ -1307,6 +1751,10 @@ static int cli_print_body(void *obj, void *arg, int flags)
 
 	ast_assert(context->output_buffer != NULL);
 
+	if (!state) {
+		return 0;
+	}
+
 	ast_str_append(&context->output_buffer, 0, " %-s/%-*.*s  %-16s  %-16s\n",
 		id,
 		(int) (REGISTRATION_URI_FIELD_LEN - strlen(id)),
@@ -1315,7 +1763,7 @@ static int cli_print_body(void *obj, void *arg, int flags)
 		AST_VECTOR_SIZE(&registration->outbound_auths)
 			? AST_VECTOR_GET(&registration->outbound_auths, 0)
 			: "n/a",
-		sip_outbound_registration_status_str[state->client_state->status]);
+		sip_outbound_registration_status_str(state->client_state->status));
 	ao2_ref(state, -1);
 
 	if (context->show_details
@@ -1338,15 +1786,18 @@ static char *my_cli_traverse_objects(struct ast_cli_entry *e, int cmd, struct as
 }
 
 static struct ast_cli_entry cli_outbound_registration[] = {
-	AST_CLI_DEFINE(cli_unregister, "Send a REGISTER request to an outbound registration target with a expiration of 0"),
+	AST_CLI_DEFINE(cli_unregister, "Unregisters outbound registration target"),
+	AST_CLI_DEFINE(cli_register, "Registers an outbound registration target"),
 	AST_CLI_DEFINE(my_cli_traverse_objects, "List PJSIP Registrations",
 		.command = "pjsip list registrations",
-		.usage = "Usage: pjsip list registrations\n"
-				 "       List the configured PJSIP Registrations\n"),
+		.usage = "Usage: pjsip list registrations [ like <pattern> ]\n"
+				"       List the configured PJSIP Registrations\n"
+				"       Optional regular expression pattern is used to filter the list.\n"),
 	AST_CLI_DEFINE(my_cli_traverse_objects, "Show PJSIP Registrations",
 		.command = "pjsip show registrations",
-		.usage = "Usage: pjsip show registrations\n"
-				 "       Show the configured PJSIP Registrations\n"),
+		.usage = "Usage: pjsip show registrations [ like <pattern> ]\n"
+				"       Show the configured PJSIP Registrations\n"
+				"       Optional regular expression pattern is used to filter the list.\n"),
 	AST_CLI_DEFINE(my_cli_traverse_objects, "Show PJSIP Registration",
 		.command = "pjsip show registration",
 		.usage = "Usage: pjsip show registration <id>\n"
@@ -1355,27 +1806,177 @@ static struct ast_cli_entry cli_outbound_registration[] = {
 
 static struct ast_sip_cli_formatter_entry *cli_formatter;
 
+static void auth_observer(const char *type)
+{
+	struct sip_outbound_registration *registration;
+	struct sip_outbound_registration_state *state;
+	struct ao2_container *regs;
+	const char *registration_id;
+	struct ao2_iterator i;
+
+	ast_debug(4, "Auths updated. Checking for any outbound registrations that are in permanent rejected state so they can be retried\n");
+
+	regs = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "registration",
+		AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+	if (!regs || ao2_container_count(regs) == 0) {
+		ao2_cleanup(regs);
+		return;
+	}
+
+	i = ao2_iterator_init(regs, 0);
+	for (; (registration = ao2_iterator_next(&i)); ao2_ref(registration, -1)) {
+		registration_id = ast_sorcery_object_get_id(registration);
+		state = get_state(registration_id);
+		if (state && state->client_state->status == SIP_REGISTRATION_REJECTED_PERMANENT) {
+			ast_debug(4, "Trying outbound registration '%s' again\n", registration_id);
+
+			if (ast_sip_push_task(state->client_state->serializer,
+					      sip_outbound_registration_perform, ao2_bump(state))) {
+				ast_log(LOG_ERROR, "Failed to perform outbound registration on '%s'\n", registration_id);
+				ao2_ref(state, -1);
+			}
+		}
+		ao2_cleanup(state);
+	}
+	ao2_iterator_destroy(&i);
+	ao2_cleanup(regs);
+}
+
+static const struct ast_sorcery_observer observer_callbacks_auth = {
+	.loaded = auth_observer,
+};
+
+static int check_state(void *obj, void *arg, int flags)
+{
+	struct sip_outbound_registration_state *state = obj;
+	struct sip_outbound_registration *registration;
+
+	registration = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "registration",
+		ast_sorcery_object_get_id(state->registration));
+	if (!registration) {
+		/* This is a dead registration */
+		return CMP_MATCH;
+	}
+
+	ao2_ref(registration, -1);
+	return 0;
+}
+
+/*!
+ * \internal
+ * \brief Observer to purge dead registration states.
+ *
+ * \param name Module name owning the sorcery instance.
+ * \param sorcery Instance being observed.
+ * \param object_type Name of object being observed.
+ * \param reloaded Non-zero if the object is being reloaded.
+ *
+ * \return Nothing
+ */
+static void registration_loaded_observer(const char *name, const struct ast_sorcery *sorcery, const char *object_type, int reloaded)
+{
+	struct ao2_container *states;
+
+	if (strcmp(object_type, "registration")) {
+		/* Not interested */
+		return;
+	}
+
+	states = ao2_global_obj_ref(current_states);
+	if (!states) {
+		/* Global container has gone.  Likely shutting down. */
+		return;
+	}
+
+	/*
+	 * Refresh the current configured registrations. We don't need to hold
+	 * onto the objects, as the apply handler will cause their states to
+	 * be created appropriately.
+	 */
+	ao2_cleanup(get_registrations());
+
+	/* Now to purge dead registrations. */
+	ao2_callback(states, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, check_state, NULL);
+	ao2_ref(states, -1);
+}
+
+static const struct ast_sorcery_instance_observer observer_callbacks_registrations = {
+	.object_type_loaded = registration_loaded_observer,
+};
+
 static int unload_module(void)
 {
-	ast_cli_unregister_multiple(cli_outbound_registration, ARRAY_LEN(cli_outbound_registration));
-	ast_sip_unregister_cli_formatter(cli_formatter);
+	int remaining;
+
 	ast_manager_unregister("PJSIPShowRegistrationsOutbound");
 	ast_manager_unregister("PJSIPUnregister");
+	ast_manager_unregister("PJSIPRegister");
+
+	ast_cli_unregister_multiple(cli_outbound_registration, ARRAY_LEN(cli_outbound_registration));
+	ast_sip_unregister_cli_formatter(cli_formatter);
+	cli_formatter = NULL;
+
+	ast_sip_unregister_endpoint_identifier(&line_identifier);
+
+	ast_sorcery_observer_remove(ast_sip_get_sorcery(), "auth", &observer_callbacks_auth);
+	ast_sorcery_instance_observer_remove(ast_sip_get_sorcery(), &observer_callbacks_registrations);
+
+	ast_sorcery_object_unregister(ast_sip_get_sorcery(), "registration");
 
 	ao2_global_obj_release(current_states);
 
+	/* 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);
+	if (remaining) {
+		/*
+		 * NOTE: We probably have a sip_outbound_registration_client_state
+		 * ref leak if the remaining count cannot reach zero after a few
+		 * minutes of trying to unload.
+		 */
+		ast_log(LOG_WARNING, "Unload incomplete.  Could not stop %d outbound registrations.  Try again later.\n",
+			remaining);
+		return -1;
+	}
+
+	ast_debug(2, "Successful shutdown.\n");
+
+	ao2_cleanup(shutdown_group);
+	shutdown_group = NULL;
+
 	return 0;
 }
 
 static int load_module(void)
 {
-	struct ao2_container *registrations, *new_states;
+	struct ao2_container *new_states;
+
 	CHECK_PJSIP_MODULE_LOADED();
 
+	shutdown_group = ast_serializer_shutdown_group_alloc();
+	if (!shutdown_group) {
+		return AST_MODULE_LOAD_FAILURE;
+	}
+
+	/* Create outbound registration states container. */
+	new_states = ao2_container_alloc(DEFAULT_STATE_BUCKETS,
+		registration_state_hash, registration_state_cmp);
+	if (!new_states) {
+		ast_log(LOG_ERROR, "Unable to allocate registration states container\n");
+		unload_module();
+		return AST_MODULE_LOAD_FAILURE;
+	}
+	ao2_global_obj_replace_unref(current_states, new_states);
+	ao2_ref(new_states, -1);
+
+	/*
+	 * Register sorcery object descriptions.
+	 */
 	ast_sorcery_apply_config(ast_sip_get_sorcery(), "res_pjsip_outbound_registration");
 	ast_sorcery_apply_default(ast_sip_get_sorcery(), "registration", "config", "pjsip.conf,criteria=type=registration");
 
 	if (ast_sorcery_object_register(ast_sip_get_sorcery(), "registration", sip_outbound_registration_alloc, NULL, sip_outbound_registration_apply)) {
+		unload_module();
 		return AST_MODULE_LOAD_DECLINE;
 	}
 
@@ -1388,19 +1989,35 @@ static int load_module(void)
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "expiration", "3600", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, expiration));
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "retry_interval", "60", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, retry_interval));
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "forbidden_retry_interval", "0", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, forbidden_retry_interval));
+	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "fatal_retry_interval", "0", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, fatal_retry_interval));
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "max_retries", "10", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, max_retries));
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "auth_rejection_permanent", "yes", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, auth_rejection_permanent));
 	ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "registration", "outbound_auth", "", outbound_auth_handler, outbound_auths_to_str, outbound_auths_to_var_list, 0, 0);
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "support_path", "no", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, support_path));
+	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "line", "no", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, line));
+	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "endpoint", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, endpoint));
+
+	/*
+	 * Register sorcery observers.
+	 */
+	if (ast_sorcery_instance_observer_add(ast_sip_get_sorcery(),
+		&observer_callbacks_registrations)
+		|| ast_sorcery_observer_add(ast_sip_get_sorcery(), "auth",
+			&observer_callbacks_auth)) {
+		ast_log(LOG_ERROR, "Unable to register observers.\n");
+		unload_module();
+		return AST_MODULE_LOAD_FAILURE;
+	}
 
-	ast_manager_register_xml("PJSIPUnregister", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_unregister);
-	ast_manager_register_xml("PJSIPShowRegistrationsOutbound", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_show_outbound_registrations);
+	/* Register how this module identifies endpoints. */
+	ast_sip_register_endpoint_identifier(&line_identifier);
 
+	/* Register CLI commands. */
 	cli_formatter = ao2_alloc(sizeof(struct ast_sip_cli_formatter_entry), NULL);
 	if (!cli_formatter) {
 		ast_log(LOG_ERROR, "Unable to allocate memory for cli formatter\n");
 		unload_module();
-		return -1;
+		return AST_MODULE_LOAD_FAILURE;
 	}
 	cli_formatter->name = "registration";
 	cli_formatter->print_header = cli_print_header;
@@ -1409,25 +2026,22 @@ static int load_module(void)
 	cli_formatter->iterate = cli_iterator;
 	cli_formatter->get_id = ast_sorcery_object_get_id;
 	cli_formatter->retrieve_by_id = cli_retrieve_by_id;
-
 	ast_sip_register_cli_formatter(cli_formatter);
 	ast_cli_register_multiple(cli_outbound_registration, ARRAY_LEN(cli_outbound_registration));
 
-	if (!(new_states = ao2_container_alloc(
-		      DEFAULT_STATE_BUCKETS, registration_state_hash, registration_state_cmp))) {
-		ast_log(LOG_ERROR, "Unable to allocate registration states container\n");
-		unload_module();
-		return AST_MODULE_LOAD_FAILURE;
-	}
-	ao2_global_obj_replace_unref(current_states, new_states);
-	ao2_ref(new_states, -1);
+	/* Register AMI actions. */
+	ast_manager_register_xml("PJSIPUnregister", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_unregister);
+	ast_manager_register_xml("PJSIPRegister", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_register);
+	ast_manager_register_xml("PJSIPShowRegistrationsOutbound", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_show_outbound_registrations);
 
-	ast_sorcery_reload_object(ast_sip_get_sorcery(), "registration");
-	if (!(registrations = get_registrations())) {
-		unload_module();
-		return AST_MODULE_LOAD_FAILURE;
-	}
-	ao2_ref(registrations, -1);
+	/* Clear any previous statsd gauges in case we weren't shutdown cleanly */
+	ast_statsd_log("PJSIP.registrations.count", AST_STATSD_GAUGE, 0);
+	ast_statsd_log("PJSIP.registrations.state.Registered", AST_STATSD_GAUGE, 0);
+	ast_statsd_log("PJSIP.registrations.state.Unregistered", AST_STATSD_GAUGE, 0);
+	ast_statsd_log("PJSIP.registrations.state.Rejected", AST_STATSD_GAUGE, 0);
+
+	/* Load configuration objects */
+	ast_sorcery_load_object(ast_sip_get_sorcery(), "registration");
 
 	return AST_MODULE_LOAD_SUCCESS;
 }
@@ -1435,7 +2049,6 @@ static int load_module(void)
 static int reload_module(void)
 {
 	ast_sorcery_reload_object(ast_sip_get_sorcery(), "registration");
-	ao2_cleanup(get_registrations());
 	return 0;
 }
 
diff --git a/res/res_pjsip_phoneprov_provider.c b/res/res_pjsip_phoneprov_provider.c
index 61b026f..8dafabc 100644
--- a/res/res_pjsip_phoneprov_provider.c
+++ b/res/res_pjsip_phoneprov_provider.c
@@ -135,6 +135,11 @@ struct phoneprov {
 static void phoneprov_destroy(void *obj)
 {
 	struct phoneprov *pp = obj;
+	char *mac = ast_var_find(pp->vars, "MAC");
+
+	if (mac) {
+		ast_phoneprov_delete_extension(AST_MODULE, mac);
+	}
 
 	ast_var_list_destroy(pp->vars);
 }
@@ -196,7 +201,7 @@ static int fields_handler(const void *obj, struct ast_variable **fields)
 	struct ast_variable *var;
 
 	AST_VAR_LIST_TRAVERSE(pp->vars, pvar) {
-		var = ast_variable_new(pvar->name, pvar->value, NULL);
+		var = ast_variable_new(pvar->name, pvar->value, "");
 		if (!var) {
 			ast_variables_destroy(head);
 			return -1;
@@ -228,7 +233,7 @@ static int load_endpoint(const char *id, const char *endpoint_name, struct varsh
 	/* We need to use res_pjsip's sorcery instance instead of our own to
 	 * get endpoint and auth.
 	 */
-	endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint",
+	endpoint = ast_sorcery_retrieve_by_id(sorcery, "endpoint",
 		endpoint_name);
 	if (!endpoint) {
 		ast_log(LOG_ERROR, "phoneprov %s contained invalid endpoint %s.\n", id,
@@ -250,7 +255,7 @@ static int load_endpoint(const char *id, const char *endpoint_name, struct varsh
 			endpoint->id.self.name.str, vars);
 	}
 
-	transport = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "transport",
+	transport = ast_sorcery_retrieve_by_id(sorcery, "transport",
 		endpoint->transport);
 	if (!transport) {
 		ast_log(LOG_ERROR, "Endpoint %s contained invalid transport %s.\n", endpoint_name,
@@ -264,7 +269,7 @@ static int load_endpoint(const char *id, const char *endpoint_name, struct varsh
 	}
 	auth_name = AST_VECTOR_GET(&endpoint->inbound_auths, 0);
 
-	auth = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "auth", auth_name);
+	auth = ast_sorcery_retrieve_by_id(sorcery, "auth", auth_name);
 	if (!auth) {
 		ast_log(LOG_ERROR, "phoneprov %s contained invalid auth %s.\n", id, auth_name);
 		return -1;
@@ -280,90 +285,82 @@ static int load_endpoint(const char *id, const char *endpoint_name, struct varsh
 	return 0;
 }
 
-/*! \brief Callback that loads the users from phoneprov sections */
-static int load_users(void)
+/*! \brief Callback that validates the phoneprov object */
+static void users_apply_handler(struct phoneprov *pp)
 {
-	struct phoneprov *pp;
-	struct ao2_container *c;
-	struct ao2_iterator i;
-	int user_count = 0;
+	const char *id = ast_sorcery_object_get_id(pp);
+	const char *endpoint_name;
 	char port_string[6];
 
-	c = ast_sorcery_retrieve_by_fields(sorcery, "phoneprov", AST_RETRIEVE_FLAG_MULTIPLE, NULL);
-	if (!c) {
-		ast_log(LOG_ERROR, "Retrieve by regex failed to allocate a container.\n");
-		return -1;
+	if (!ast_var_find(pp->vars,
+		ast_phoneprov_std_variable_lookup(AST_PHONEPROV_STD_MAC))) {
+		ast_log(LOG_ERROR, "phoneprov %s must contain a MAC entry.\n", id);
+		return;
 	}
-	if (ao2_container_count(c) == 0) {
-		ast_log(LOG_ERROR, "Unable to find any phoneprov users.\n");
-		ao2_cleanup(c);
-		return -1;
+
+	if (!ast_var_find(pp->vars,
+		ast_phoneprov_std_variable_lookup(AST_PHONEPROV_STD_PROFILE))) {
+		ast_log(LOG_ERROR, "phoneprov %s must contain a PROFILE entry.\n", id);
+		return;
 	}
 
-	i = ao2_iterator_init(c, 0);
-	while ((pp = ao2_iterator_next(&i))) {
-		const char *endpoint_name;
-		const char *id = ast_sorcery_object_get_id(pp);
-
-		endpoint_name = ast_var_find(pp->vars, "endpoint");
-		if (endpoint_name) {
-			if (load_endpoint(id, endpoint_name, pp->vars, port_string)) {
-				goto cleanup;
-			}
+	endpoint_name = ast_var_find(pp->vars, "endpoint");
+	if (endpoint_name) {
+		if (load_endpoint(id, endpoint_name, pp->vars, port_string)) {
+			return;
 		}
+	}
 
-		if (!ast_var_find(pp->vars,
-			ast_phoneprov_std_variable_lookup(AST_PHONEPROV_STD_USERNAME))) {
-			assign_and_insert(
-				ast_phoneprov_std_variable_lookup(AST_PHONEPROV_STD_USERNAME), id,
-				pp->vars);
-		}
+	if (!ast_var_find(pp->vars,
+		ast_phoneprov_std_variable_lookup(AST_PHONEPROV_STD_USERNAME))) {
+		assign_and_insert(
+			ast_phoneprov_std_variable_lookup(AST_PHONEPROV_STD_USERNAME), id,
+			pp->vars);
+	}
 
-		if (!ast_var_find(pp->vars,
-			ast_phoneprov_std_variable_lookup(AST_PHONEPROV_STD_LABEL))) {
-			assign_and_insert(ast_phoneprov_std_variable_lookup(AST_PHONEPROV_STD_LABEL),
-				id, pp->vars);
-		}
+	if (!ast_var_find(pp->vars,
+		ast_phoneprov_std_variable_lookup(AST_PHONEPROV_STD_LABEL))) {
+		assign_and_insert(ast_phoneprov_std_variable_lookup(AST_PHONEPROV_STD_LABEL),
+			id, pp->vars);
+	}
 
-		if (!ast_var_find(pp->vars,
-			ast_phoneprov_std_variable_lookup(AST_PHONEPROV_STD_SERVER_PORT))) {
-			assign_and_insert("SERVER_PORT", S_OR(port_string, "5060"), pp->vars);
-		}
+	if (!ast_var_find(pp->vars,
+		ast_phoneprov_std_variable_lookup(AST_PHONEPROV_STD_SERVER_PORT))) {
+		assign_and_insert("SERVER_PORT", S_OR(port_string, "5060"), pp->vars);
+	}
 
-		if (!ast_var_find(pp->vars,
-			ast_phoneprov_std_variable_lookup(AST_PHONEPROV_STD_PROFILE))) {
-			ast_log(LOG_ERROR, "phoneprov %s didn't contain a PROFILE entry.\n", id);
-		} else if (!ast_phoneprov_add_extension(AST_MODULE, pp->vars)) {
-			user_count++;
-		}
-		ao2_ref(pp, -1);
+	if (!ast_var_find(pp->vars,
+		ast_phoneprov_std_variable_lookup(AST_PHONEPROV_STD_PROFILE))) {
+		ast_log(LOG_ERROR, "phoneprov %s didn't contain a PROFILE entry.\n", id);
 	}
 
-cleanup:
-	ao2_iterator_destroy(&i);
-	ao2_cleanup(pp);
-	ao2_cleanup(c);
+	ast_phoneprov_add_extension(AST_MODULE, pp->vars);
 
-	return user_count > 0 ? 0 : -1;
+	return;
 }
 
-/*! \brief Callback that validates the phoneprov object */
-static int users_apply_handler(const struct ast_sorcery *sorcery, void *obj)
+/*! \brief Callback that loads the users from phoneprov sections */
+static int load_users(void)
 {
-	struct phoneprov *pp = obj;
-	const char *id = ast_sorcery_object_get_id(pp);
+	struct ao2_container *users;
+	struct ao2_iterator i;
+	struct phoneprov *pp;
 
-	if (!ast_var_find(pp->vars,
-		ast_phoneprov_std_variable_lookup(AST_PHONEPROV_STD_MAC))) {
-		ast_log(LOG_ERROR, "phoneprov %s must contain a MAC entry.\n", id);
-		return -1;
+	ast_sorcery_reload_object(sorcery, "phoneprov");
+
+	users = ast_sorcery_retrieve_by_fields(sorcery, "phoneprov",
+		AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+	if (!users) {
+		return 0;
 	}
 
-	if (!ast_var_find(pp->vars,
-		ast_phoneprov_std_variable_lookup(AST_PHONEPROV_STD_PROFILE))) {
-		ast_log(LOG_ERROR, "phoneprov %s must contain a PROFILE entry.\n", id);
-		return -1;
+	i = ao2_iterator_init(users, 0);
+	while ((pp = ao2_iterator_next(&i))) {
+		users_apply_handler(pp);
+		ao2_ref(pp, -1);
 	}
+	ao2_iterator_destroy(&i);
+	ao2_ref(users, -1);
 
 	return 0;
 }
@@ -372,23 +369,24 @@ static int load_module(void)
 {
 	CHECK_PJSIP_MODULE_LOADED();
 
-	if (!(sorcery = ast_sorcery_open())) {
-		ast_log(LOG_ERROR, "Unable to open a sorcery instance.\n");
-		return AST_MODULE_LOAD_DECLINE;
-	}
+	sorcery = ast_sip_get_sorcery();
 
-	ast_sorcery_apply_default(sorcery, "phoneprov", "config", "pjsip.conf,criteria=type=phoneprov");
+	ast_sorcery_apply_config(sorcery, "res_pjsip_phoneprov_provider");
+	ast_sorcery_apply_default(sorcery, "phoneprov", "config",
+		"pjsip.conf,criteria=type=phoneprov");
 
 	ast_sorcery_object_register(sorcery, "phoneprov", phoneprov_alloc, NULL,
-		users_apply_handler);
-	ast_sorcery_object_field_register(sorcery, "phoneprov", "type", "", OPT_NOOP_T, 0, 0);
+		NULL);
+
+	ast_sorcery_object_field_register(sorcery, "phoneprov", "type", "", OPT_NOOP_T, 0,
+		0);
 	ast_sorcery_object_fields_register(sorcery, "phoneprov", "^", aco_handler,
 		fields_handler);
-	ast_sorcery_reload_object(sorcery, "phoneprov");
+
+	ast_sorcery_load_object(sorcery, "phoneprov");
 
 	if (ast_phoneprov_provider_register(AST_MODULE, load_users)) {
 		ast_log(LOG_ERROR, "Unable to register pjsip phoneprov provider.\n");
-		ast_sorcery_unref(sorcery);
 		return AST_MODULE_LOAD_DECLINE;
 	}
 
@@ -398,15 +396,18 @@ static int load_module(void)
 static int unload_module(void)
 {
 	ast_phoneprov_provider_unregister(AST_MODULE);
-	ast_sorcery_unref(sorcery);
 
 	return 0;
 }
 
 static int reload_module(void)
 {
-	unload_module();
-	load_module();
+	ast_phoneprov_provider_unregister(AST_MODULE);
+
+	if (ast_phoneprov_provider_register(AST_MODULE, load_users)) {
+		ast_log(LOG_ERROR, "Unable to register pjsip phoneprov provider.\n");
+		return AST_MODULE_LOAD_DECLINE;
+	}
 
 	return 0;
 }
diff --git a/res/res_pjsip_pidf_body_generator.c b/res/res_pjsip_pidf_body_generator.c
index ef0cce5..d3be8c1 100644
--- a/res/res_pjsip_pidf_body_generator.c
+++ b/res/res_pjsip_pidf_body_generator.c
@@ -84,19 +84,18 @@ static int pidf_generate_body_content(void *body, void *data)
 
 static void pidf_to_string(void *body, struct ast_str **str)
 {
-	int size;
-	int growths = 0;
 	pjpidf_pres *pres = body;
+	int growths = 0;
+	int size;
 
 	do {
 		size = pjpidf_print(pres, ast_str_buffer(*str), ast_str_size(*str) - 1);
-		if (size == AST_PJSIP_XML_PROLOG_LEN) {
+		if (size <= AST_PJSIP_XML_PROLOG_LEN) {
 			ast_str_make_space(str, ast_str_size(*str) * 2);
 			++growths;
 		}
-	} while (size == AST_PJSIP_XML_PROLOG_LEN && growths < MAX_STRING_GROWTHS);
-
-	if (size == AST_PJSIP_XML_PROLOG_LEN) {
+	} while (size <= AST_PJSIP_XML_PROLOG_LEN && growths < MAX_STRING_GROWTHS);
+	if (size <= AST_PJSIP_XML_PROLOG_LEN) {
 		ast_log(LOG_WARNING, "PIDF body text too large\n");
 		return;
 	}
diff --git a/res/res_pjsip_pidf_digium_body_supplement.c b/res/res_pjsip_pidf_digium_body_supplement.c
index 86e673a..4b1a781 100644
--- a/res/res_pjsip_pidf_digium_body_supplement.c
+++ b/res/res_pjsip_pidf_digium_body_supplement.c
@@ -40,7 +40,7 @@ static int pidf_supplement_body(void *body, void *data)
 {
 	struct ast_sip_exten_state_data *state_data = data;
 	pj_xml_node *node;
-	char sanitized[256];
+	char sanitized[1024];
 
 	if (ast_strlen_zero(state_data->user_agent) ||
 	    !strstr(state_data->user_agent, "digium")) {
diff --git a/res/res_pjsip_publish_asterisk.c b/res/res_pjsip_publish_asterisk.c
index e1b095e..c0d3b90 100644
--- a/res/res_pjsip_publish_asterisk.c
+++ b/res/res_pjsip_publish_asterisk.c
@@ -858,6 +858,7 @@ static int load_module(void)
 {
 	CHECK_PJSIP_PUBSUB_MODULE_LOADED();
 
+	ast_sorcery_apply_config(ast_sip_get_sorcery(), "asterisk-publication");
 	ast_sorcery_apply_default(ast_sip_get_sorcery(), "asterisk-publication", "config", "pjsip.conf,criteria=type=asterisk-publication");
 
 	if (ast_sorcery_object_register(ast_sip_get_sorcery(), "asterisk-publication", asterisk_publication_config_alloc, NULL, NULL)) {
diff --git a/res/res_pjsip_pubsub.c b/res/res_pjsip_pubsub.c
index 02deeb6..99376b1 100644
--- a/res/res_pjsip_pubsub.c
+++ b/res/res_pjsip_pubsub.c
@@ -148,6 +148,9 @@
 							<enum name="presence"><para>
 								Device state and presence reporting.
 							</para></enum>
+							<enum name="dialog"><para>
+								This is identical to <replaceable>presence</replaceable>.
+							</para></enum>
 							<enum name="message-summary"><para>
 								Message-waiting indication (MWI) reporting.
 							</para></enum>
@@ -408,6 +411,8 @@ struct sip_subscription_tree {
 	int is_list;
 	/*! Next item in the list */
 	AST_LIST_ENTRY(sip_subscription_tree) next;
+	/*! Indicates that a NOTIFY is currently being sent on the SIP subscription */
+	int last_notify;
 };
 
 /*!
@@ -558,8 +563,19 @@ 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));
 
-		ast_copy_string(sub_tree->persistence->packet, rdata->pkt_info.packet,
-				sizeof(sub_tree->persistence->packet));
+		/* 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
+		 * will always point to the proper SIP message that is to be processed. When updating subscription
+		 * persistence that is pulled from persistent storage, though, the rdata->pkt_info.packet will
+		 * only ever have a single SIP message on it, and so we base persistence on that.
+		 */
+		if (rdata->msg_info.msg_buf) {
+			ast_copy_string(sub_tree->persistence->packet, rdata->msg_info.msg_buf,
+					MIN(sizeof(sub_tree->persistence->packet), rdata->msg_info.len));
+		} else {
+			ast_copy_string(sub_tree->persistence->packet, rdata->pkt_info.packet,
+					sizeof(sub_tree->persistence->packet));
+		}
 		ast_copy_string(sub_tree->persistence->src_name, rdata->pkt_info.src_name,
 				sizeof(sub_tree->persistence->src_name));
 		sub_tree->persistence->src_port = rdata->pkt_info.src_port;
@@ -582,6 +598,7 @@ static void subscription_persistence_remove(struct sip_subscription_tree *sub_tr
 
 	ast_sorcery_delete(ast_sip_get_sorcery(), sub_tree->persistence);
 	ao2_ref(sub_tree->persistence, -1);
+	sub_tree->persistence = NULL;
 }
 
 
@@ -949,7 +966,7 @@ static void resource_tree_destroy(struct resource_tree *tree)
 static int build_resource_tree(struct ast_sip_endpoint *endpoint, const struct ast_sip_subscription_handler *handler,
 		const char *resource, struct resource_tree *tree, int has_eventlist_support)
 {
-	struct resource_list *list;
+	RAII_VAR(struct resource_list *, list, NULL, ao2_cleanup);
 	struct resources visited;
 
 	if (!has_eventlist_support || !(list = retrieve_resource_list(resource, handler->event_name))) {
@@ -968,6 +985,7 @@ static int build_resource_tree(struct ast_sip_endpoint *endpoint, const struct a
 
 	tree->root = tree_node_alloc(resource, &visited, list->full_state);
 	if (!tree->root) {
+		AST_VECTOR_FREE(&visited);
 		return 500;
 	}
 
@@ -975,7 +993,6 @@ static int build_resource_tree(struct ast_sip_endpoint *endpoint, const struct a
 
 	build_node_children(endpoint, handler, list, tree->root, &visited);
 	AST_VECTOR_FREE(&visited);
-	ao2_cleanup(list);
 
 	if (AST_VECTOR_SIZE(&tree->root->children) > 0) {
 		return 200;
@@ -1006,25 +1023,6 @@ static int datastore_cmp(void *obj, void *arg, int flags)
 	return strcmp(datastore1->uid, uid2) ? 0 : CMP_MATCH | CMP_STOP;
 }
 
-static int subscription_remove_serializer(void *obj)
-{
-	struct sip_subscription_tree *sub_tree = obj;
-
-	/* This is why we keep the dialog on the subscription. When the subscription
-	 * is destroyed, there is no guarantee that the underlying dialog is ready
-	 * to be destroyed. Furthermore, there's no guarantee in the opposite direction
-	 * either. The dialog could be destroyed before our subscription is. We fix
-	 * this problem by keeping a reference to the dialog until it is time to
-	 * destroy the subscription. We need to have the dialog available when the
-	 * subscription is destroyed so that we can guarantee that our attempt to
-	 * remove the serializer will be successful.
-	 */
-	ast_sip_dialog_set_serializer(sub_tree->dlg, NULL);
-	pjsip_dlg_dec_session(sub_tree->dlg, &pubsub_module);
-
-	return 0;
-}
-
 static void add_subscription(struct sip_subscription_tree *obj)
 {
 	SCOPED_LOCK(lock, &subscriptions, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK);
@@ -1048,14 +1046,32 @@ static void remove_subscription(struct sip_subscription_tree *obj)
 	AST_RWLIST_TRAVERSE_SAFE_END;
 }
 
-static void subscription_destructor(void *obj)
+static void destroy_subscription(struct ast_sip_subscription *sub)
 {
-	struct ast_sip_subscription *sub = obj;
-
 	ast_debug(3, "Destroying SIP subscription to resource %s\n", sub->resource);
 	ast_free(sub->body_text);
 
+	AST_VECTOR_FREE(&sub->children);
 	ao2_cleanup(sub->datastores);
+	ast_free(sub);
+}
+
+static void destroy_subscriptions(struct ast_sip_subscription *root)
+{
+	int i;
+
+	if (!root) {
+		return;
+	}
+
+	for (i = 0; i < AST_VECTOR_SIZE(&root->children); ++i) {
+		struct ast_sip_subscription *child;
+
+		child = AST_VECTOR_GET(&root->children, i);
+		destroy_subscriptions(child);
+	}
+
+	destroy_subscription(root);
 }
 
 static struct ast_sip_subscription *allocate_subscription(const struct ast_sip_subscription_handler *handler,
@@ -1064,7 +1080,7 @@ static struct ast_sip_subscription *allocate_subscription(const struct ast_sip_s
 	struct ast_sip_subscription *sub;
 	pjsip_sip_uri *contact_uri;
 
-	sub = ao2_alloc(sizeof(*sub) + strlen(resource) + 1, subscription_destructor);
+	sub = ast_calloc(1, sizeof(*sub) + strlen(resource) + 1);
 	if (!sub) {
 		return NULL;
 	}
@@ -1072,13 +1088,13 @@ static struct ast_sip_subscription *allocate_subscription(const struct ast_sip_s
 
 	sub->datastores = ao2_container_alloc(DATASTORE_BUCKETS, datastore_hash, datastore_cmp);
 	if (!sub->datastores) {
-		ao2_ref(sub, -1);
+		destroy_subscription(sub);
 		return NULL;
 	}
 
 	sub->body_text = ast_str_create(128);
 	if (!sub->body_text) {
-		ao2_ref(sub, -1);
+		destroy_subscription(sub);
 		return NULL;
 	}
 
@@ -1089,7 +1105,7 @@ static struct ast_sip_subscription *allocate_subscription(const struct ast_sip_s
 
 	sub->handler = handler;
 	sub->subscription_state = PJSIP_EVSUB_STATE_ACTIVE;
-	sub->tree = tree;
+	sub->tree = ao2_bump(tree);
 
 	return sub;
 }
@@ -1117,6 +1133,7 @@ static struct ast_sip_subscription *create_virtual_subscriptions(const struct as
 
 	sub->full_state = current->full_state;
 	sub->body_generator = generator;
+	AST_VECTOR_INIT(&sub->children, AST_VECTOR_SIZE(&current->children));
 
 	for (i = 0; i < AST_VECTOR_SIZE(&current->children); ++i) {
 		struct ast_sip_subscription *child;
@@ -1151,45 +1168,67 @@ static void shutdown_subscriptions(struct ast_sip_subscription *sub)
 	if (AST_VECTOR_SIZE(&sub->children) > 0) {
 		for (i = 0; i < AST_VECTOR_SIZE(&sub->children); ++i) {
 			shutdown_subscriptions(AST_VECTOR_GET(&sub->children, i));
-			ao2_cleanup(AST_VECTOR_GET(&sub->children, i));
 		}
 		return;
 	}
 
+	/* We notify subscription shutdown only on the tree leaves. */
 	if (sub->handler->subscription_shutdown) {
 		sub->handler->subscription_shutdown(sub);
 	}
 }
+static int subscription_unreference_dialog(void *obj)
+{
+	struct sip_subscription_tree *sub_tree = obj;
+
+	/* This is why we keep the dialog on the subscription. When the subscription
+	 * is destroyed, there is no guarantee that the underlying dialog is ready
+	 * to be destroyed. Furthermore, there's no guarantee in the opposite direction
+	 * either. The dialog could be destroyed before our subscription is. We fix
+	 * this problem by keeping a reference to the dialog until it is time to
+	 * destroy the subscription. We need to have the dialog available when the
+	 * subscription is destroyed so that we can guarantee that our attempt to
+	 * remove the serializer will be successful.
+	 */
+	pjsip_dlg_dec_session(sub_tree->dlg, &pubsub_module);
+	sub_tree->dlg = NULL;
+
+	return 0;
+}
 
 static void subscription_tree_destructor(void *obj)
 {
 	struct sip_subscription_tree *sub_tree = obj;
 
+	ast_debug(3, "Destroying subscription tree %p\n", sub_tree);
+
 	remove_subscription(sub_tree);
 
-	subscription_persistence_remove(sub_tree);
 	ao2_cleanup(sub_tree->endpoint);
 
+	destroy_subscriptions(sub_tree->root);
+
 	if (sub_tree->dlg) {
-		ast_sip_push_task_synchronous(NULL, subscription_remove_serializer, sub_tree);
+		ast_sip_push_task_synchronous(sub_tree->serializer, subscription_unreference_dialog, sub_tree);
 	}
 
-	shutdown_subscriptions(sub_tree->root);
-	ao2_cleanup(sub_tree->root);
-
 	ast_taskprocessor_unreference(sub_tree->serializer);
 	ast_module_unref(ast_module_info->self);
 }
 
+void ast_sip_subscription_destroy(struct ast_sip_subscription *sub)
+{
+	ast_debug(3, "Removing subscription %p reference to subscription tree %p\n", sub, sub->tree);
+	ao2_cleanup(sub->tree);
+}
+
 static void subscription_setup_dialog(struct sip_subscription_tree *sub_tree, pjsip_dialog *dlg)
 {
-	/* We keep a reference to the dialog until our subscription is destroyed. See
-	 * the subscription_destructor for more details
-	 */
-	pjsip_dlg_inc_session(dlg, &pubsub_module);
 	sub_tree->dlg = dlg;
 	ast_sip_dialog_set_serializer(dlg, sub_tree->serializer);
+	ast_sip_dialog_set_endpoint(dlg, sub_tree->endpoint);
 	pjsip_evsub_set_mod_data(sub_tree->evsub, pubsub_module.id, sub_tree);
+	pjsip_dlg_inc_session(dlg, &pubsub_module);
 }
 
 static struct sip_subscription_tree *allocate_subscription_tree(struct ast_sip_endpoint *endpoint)
@@ -1247,7 +1286,7 @@ static struct sip_subscription_tree *create_subscription_tree(const struct ast_s
 
 	sub_tree = allocate_subscription_tree(endpoint);
 	if (!sub_tree) {
-		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL);
+		*dlg_status = PJ_ENOMEM;
 		return NULL;
 	}
 	sub_tree->role = AST_SIP_NOTIFIER;
@@ -1289,6 +1328,9 @@ static struct sip_subscription_tree *create_subscription_tree(const struct ast_s
 	return sub_tree;
 }
 
+static int initial_notify_task(void *obj);
+static int send_notify(struct sip_subscription_tree *sub_tree, unsigned int force_full_state);
+
 /*! \brief Callback function to perform the actual recreation of a subscription */
 static int subscription_persistence_recreate(void *obj, void *arg, int flags)
 {
@@ -1331,9 +1373,16 @@ static int subscription_persistence_recreate(void *obj, void *arg, int flags)
 		return 0;
 	}
 
+	if (rdata.msg_info.msg->type != PJSIP_REQUEST_MSG) {
+		ast_log(LOG_NOTICE, "Endpoint %s persisted a SIP response instead of a subscribe request. Unable to reload subscription.\n",
+				ast_sorcery_object_get_id(endpoint));
+		ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
+		return 0;
+	}
+
 	request_uri = pjsip_uri_get_uri(rdata.msg_info.msg->line.req.uri);
 	resource_size = pj_strlen(&request_uri->user) + 1;
-	resource = alloca(resource_size);
+	resource = ast_alloca(resource_size);
 	ast_copy_pj_str(resource, &request_uri->user, resource_size);
 
 	/* Update the expiration header with the new expiration */
@@ -1377,6 +1426,10 @@ static int subscription_persistence_recreate(void *obj, void *arg, int flags)
 		}
 		sub_tree->persistence = ao2_bump(persistence);
 		subscription_persistence_update(sub_tree, &rdata);
+		if (ast_sip_push_task(sub_tree->serializer, initial_notify_task, ao2_bump(sub_tree))) {
+			pjsip_evsub_terminate(sub_tree->evsub, PJ_TRUE);
+			ao2_ref(sub_tree, -1);
+		}
 	} else {
 		ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
 	}
@@ -1466,7 +1519,11 @@ static void sip_subscription_to_ami(struct sip_subscription_tree *sub_tree,
 	ast_str_append(buf, 0, "Endpoint: %s\r\n",
 		       ast_sorcery_object_get_id(sub_tree->endpoint));
 
-	ast_copy_pj_str(str, &sub_tree->dlg->call_id->id, sizeof(str));
+	if (sub_tree->dlg) {
+		ast_copy_pj_str(str, &sub_tree->dlg->call_id->id, sizeof(str));
+	} else {
+		ast_copy_string(str, "<unknown>", sizeof(str));
+	}
 	ast_str_append(buf, 0, "Callid: %s\r\n", str);
 
 	ast_str_append(buf, 0, "State: %s\r\n", pjsip_evsub_get_state_name(sub_tree->evsub));
@@ -1487,15 +1544,18 @@ static void sip_subscription_to_ami(struct sip_subscription_tree *sub_tree,
 
 void *ast_sip_subscription_get_header(const struct ast_sip_subscription *sub, const char *header)
 {
-	pjsip_dialog *dlg = sub->tree->dlg;
-	pjsip_msg *msg = ast_sip_mod_data_get(dlg->mod_data, pubsub_module.id, MOD_DATA_MSG);
+	pjsip_dialog *dlg;
+	pjsip_msg *msg;
 	pj_str_t name;
 
+	dlg = sub->tree->dlg;
+	msg = ast_sip_mod_data_get(dlg->mod_data, pubsub_module.id, MOD_DATA_MSG);
 	pj_cstr(&name, header);
 
 	return pjsip_msg_find_hdr_by_name(msg, &name, NULL);
 }
 
+/* XXX This function is not used. */
 struct ast_sip_subscription *ast_sip_create_subscription(const struct ast_sip_subscription_handler *handler,
 		struct ast_sip_endpoint *endpoint, const char *resource)
 {
@@ -1622,6 +1682,7 @@ static int sip_subscription_send_request(struct sip_subscription_tree *sub_tree,
 #ifdef TEST_FRAMEWORK
 	struct ast_sip_endpoint *endpoint = sub_tree->endpoint;
 #endif
+	pjsip_evsub *evsub = sub_tree->evsub;
 	int res;
 
 	if (allocate_tdata_buffer(tdata)) {
@@ -1629,13 +1690,13 @@ static int sip_subscription_send_request(struct sip_subscription_tree *sub_tree,
 		return -1;
 	}
 
-	res = pjsip_evsub_send_request(sub_tree->evsub, tdata) == PJ_SUCCESS ? 0 : -1;
+	res = pjsip_evsub_send_request(evsub, tdata) == PJ_SUCCESS ? 0 : -1;
 	subscription_persistence_update(sub_tree, NULL);
 
 	ast_test_suite_event_notify("SUBSCRIPTION_STATE_SET",
 		"StateText: %s\r\n"
 		"Endpoint: %s\r\n",
-		pjsip_evsub_get_state_name(sub_tree->evsub),
+		pjsip_evsub_get_state_name(evsub),
 		ast_sorcery_object_get_id(endpoint));
 
 	return res;
@@ -1758,7 +1819,7 @@ static int rlmi_print_body(struct pjsip_msg_body *msg_body, char *buf, pj_size_t
 	pj_xml_node *rlmi = msg_body->data;
 
 	num_printed = pj_xml_print(rlmi, buf, size, PJ_TRUE);
-	if (num_printed == AST_PJSIP_XML_PROLOG_LEN) {
+	if (num_printed <= AST_PJSIP_XML_PROLOG_LEN) {
 		return -1;
 	}
 
@@ -2043,6 +2104,8 @@ static pjsip_require_hdr *create_require_eventlist(pj_pool_t *pool)
 /*!
  * \brief Send a NOTIFY request to a subscriber
  *
+ * \pre sub_tree->dlg is locked
+ *
  * \param sub_tree The subscription tree representing the subscription
  * \param force_full_state If true, ignore resource list settings and send full resource list state.
  * \retval 0 Success
@@ -2053,6 +2116,12 @@ static int send_notify(struct sip_subscription_tree *sub_tree, unsigned int forc
 	pjsip_evsub *evsub = sub_tree->evsub;
 	pjsip_tx_data *tdata;
 
+	if (ast_shutdown_final()
+		&& sub_tree->root->subscription_state == PJSIP_EVSUB_STATE_TERMINATED
+		&& sub_tree->persistence) {
+		return 0;
+	}
+
 	if (pjsip_evsub_notify(evsub, sub_tree->root->subscription_state,
 				NULL, NULL, &tdata) != PJ_SUCCESS) {
 		return -1;
@@ -2069,6 +2138,9 @@ static int send_notify(struct sip_subscription_tree *sub_tree, unsigned int forc
 		pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *) require);
 	}
 
+	if (sub_tree->root->subscription_state == PJSIP_EVSUB_STATE_TERMINATED) {
+		sub_tree->last_notify = 1;
+	}
 	if (sip_subscription_send_request(sub_tree, tdata)) {
 		return -1;
 	}
@@ -2081,13 +2153,16 @@ static int send_notify(struct sip_subscription_tree *sub_tree, unsigned int forc
 static int serialized_send_notify(void *userdata)
 {
 	struct sip_subscription_tree *sub_tree = userdata;
+	pjsip_dialog *dlg = sub_tree->dlg;
 
+	pjsip_dlg_inc_lock(dlg);
 	/* It's possible that between when the notification was scheduled
 	 * and now, that a new SUBSCRIBE arrived, requiring full state to be
 	 * sent out in an immediate NOTIFY. If that has happened, we need to
 	 * bail out here instead of sending the batched NOTIFY.
 	 */
 	if (!sub_tree->send_scheduled_notify) {
+		pjsip_dlg_dec_lock(dlg);
 		ao2_cleanup(sub_tree);
 		return 0;
 	}
@@ -2097,6 +2172,7 @@ static int serialized_send_notify(void *userdata)
 			"Resource: %s",
 			sub_tree->root->resource);
 	sub_tree->notify_sched_id = -1;
+	pjsip_dlg_dec_lock(dlg);
 	ao2_cleanup(sub_tree);
 	return 0;
 }
@@ -2129,8 +2205,19 @@ static int schedule_notification(struct sip_subscription_tree *sub_tree)
 int ast_sip_subscription_notify(struct ast_sip_subscription *sub, struct ast_sip_body_data *notify_data,
 		int terminate)
 {
+	int res;
+	pjsip_dialog *dlg = sub->tree->dlg;
+
+	pjsip_dlg_inc_lock(dlg);
+
+	if (!sub->tree->evsub) {
+		pjsip_dlg_dec_lock(dlg);
+		return 0;
+	}
+
 	if (ast_sip_pubsub_generate_body_content(ast_sip_subscription_get_body_type(sub),
 				ast_sip_subscription_get_body_subtype(sub), notify_data, &sub->body_text)) {
+		pjsip_dlg_dec_lock(dlg);
 		return -1;
 	}
 
@@ -2140,9 +2227,8 @@ int ast_sip_subscription_notify(struct ast_sip_subscription *sub, struct ast_sip
 	}
 
 	if (sub->tree->notification_batch_interval) {
-		return schedule_notification(sub->tree);
+		res = schedule_notification(sub->tree);
 	} else {
-		int res;
 		/* See the note in pubsub_on_rx_refresh() for why sub->tree is refbumped here */
 		ao2_ref(sub->tree, +1);
 		res = send_notify(sub->tree, 0);
@@ -2150,9 +2236,10 @@ int ast_sip_subscription_notify(struct ast_sip_subscription *sub, struct ast_sip
 				"Resource: %s",
 				sub->tree->root->resource);
 		ao2_ref(sub->tree, -1);
-
-		return res;
 	}
+
+	pjsip_dlg_dec_lock(dlg);
+	return res;
 }
 
 void ast_sip_subscription_get_local_uri(struct ast_sip_subscription *sub, char *buf, size_t size)
@@ -2162,7 +2249,9 @@ void ast_sip_subscription_get_local_uri(struct ast_sip_subscription *sub, char *
 
 void ast_sip_subscription_get_remote_uri(struct ast_sip_subscription *sub, char *buf, size_t size)
 {
-	pjsip_dialog *dlg = sub->tree->dlg;
+	pjsip_dialog *dlg;
+
+	dlg = sub->tree->dlg;
 	ast_copy_pj_str(buf, &dlg->remote.info_str, size);
 }
 
@@ -2171,6 +2260,11 @@ const char *ast_sip_subscription_get_resource_name(struct ast_sip_subscription *
 	return sub->resource;
 }
 
+int ast_sip_subscription_is_terminated(const struct ast_sip_subscription *sub)
+{
+	return sub->subscription_state == PJSIP_EVSUB_STATE_TERMINATED ? 1 : 0;
+}
+
 static int sip_subscription_accept(struct sip_subscription_tree *sub_tree, pjsip_rx_data *rdata, int response)
 {
 	pjsip_hdr res_hdr;
@@ -2481,6 +2575,7 @@ static int generate_initial_notify(struct ast_sip_subscription *sub)
 		return 0;
 	}
 
+	/* We notify subscription establishment only on the tree leaves. */
 	if (sub->handler->notifier->subscription_established(sub)) {
 		return -1;
 	}
@@ -2500,6 +2595,24 @@ static int generate_initial_notify(struct ast_sip_subscription *sub)
 	return res;
 }
 
+static int initial_notify_task(void * obj)
+{
+	struct sip_subscription_tree *sub_tree;
+
+	sub_tree = obj;
+	if (generate_initial_notify(sub_tree->root)) {
+		pjsip_evsub_terminate(sub_tree->evsub, PJ_TRUE);
+	} else {
+		send_notify(sub_tree, 1);
+		ast_test_suite_event_notify("SUBSCRIPTION_ESTABLISHED",
+			"Resource: %s",
+			sub_tree->root->resource);
+	}
+
+	ao2_ref(sub_tree, -1);
+	return 0;
+}
+
 static pj_bool_t pubsub_on_rx_subscribe_request(pjsip_rx_data *rdata)
 {
 	pjsip_expires_hdr *expires_header;
@@ -2537,7 +2650,7 @@ static pj_bool_t pubsub_on_rx_subscribe_request(pjsip_rx_data *rdata)
 
 	request_uri_sip = pjsip_uri_get_uri(request_uri);
 	resource_size = pj_strlen(&request_uri_sip->user) + 1;
-	resource = alloca(resource_size);
+	resource = ast_alloca(resource_size);
 	ast_copy_pj_str(resource, &request_uri_sip->user, resource_size);
 
 	expires_header = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, rdata->msg_info.msg->hdr.next);
@@ -2587,13 +2700,10 @@ static pj_bool_t pubsub_on_rx_subscribe_request(pjsip_rx_data *rdata)
 		sub_tree->persistence = subscription_persistence_create(sub_tree);
 		subscription_persistence_update(sub_tree, rdata);
 		sip_subscription_accept(sub_tree, rdata, resp);
-		if (generate_initial_notify(sub_tree->root)) {
+		if (ast_sip_push_task(sub_tree->serializer, initial_notify_task, ao2_bump(sub_tree))) {
 			pjsip_evsub_terminate(sub_tree->evsub, PJ_TRUE);
+			ao2_ref(sub_tree, -1);
 		}
-		send_notify(sub_tree, 1);
-		ast_test_suite_event_notify("SUBSCRIPTION_ESTABLISHED",
-				"Resource: %s",
-				sub_tree->root->resource);
 	}
 
 	resource_tree_destroy(&tree);
@@ -2753,7 +2863,7 @@ static struct ast_sip_publication *publish_request_initial(struct ast_sip_endpoi
 
 	request_uri_sip = pjsip_uri_get_uri(request_uri);
 	resource_size = pj_strlen(&request_uri_sip->user) + 1;
-	resource_name = alloca(resource_size);
+	resource_name = ast_alloca(resource_size);
 	ast_copy_pj_str(resource_name, &request_uri_sip->user, resource_size);
 
 	resource = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "inbound-publication", resource_name);
@@ -2953,16 +3063,13 @@ int ast_sip_pubsub_register_body_generator(struct ast_sip_pubsub_body_generator
 	AST_LIST_INSERT_HEAD(&body_generators, generator, list);
 	AST_RWLIST_UNLOCK(&body_generators);
 
-	/* Lengths of type and subtype plus space for a slash. pj_str_t is not
-	 * null-terminated, so there is no need to allocate for the extra null
-	 * byte
-	 */
+	/* Lengths of type and subtype plus a slash. */
 	accept_len = strlen(generator->type) + strlen(generator->subtype) + 1;
 
-	accept.ptr = alloca(accept_len);
-	accept.slen = accept_len;
-	/* Safe use of sprintf */
-	sprintf(accept.ptr, "%s/%s", generator->type, generator->subtype);
+	/* Add room for null terminator that sprintf() will set. */
+	pj_strset(&accept, ast_alloca(accept_len + 1), accept_len);
+	sprintf((char *) pj_strbuf(&accept), "%s/%s", generator->type, generator->subtype);/* Safe */
+
 	pjsip_endpt_add_capability(ast_sip_get_pjsip_endpoint(), &pubsub_module,
 			PJSIP_H_ACCEPT, NULL, 1, &accept);
 
@@ -3083,10 +3190,88 @@ static pj_bool_t pubsub_on_rx_request(pjsip_rx_data *rdata)
 	return PJ_FALSE;
 }
 
+static void set_state_terminated(struct ast_sip_subscription *sub)
+{
+	int i;
+
+	sub->subscription_state = PJSIP_EVSUB_STATE_TERMINATED;
+	for (i = 0; i < AST_VECTOR_SIZE(&sub->children); ++i) {
+		set_state_terminated(AST_VECTOR_GET(&sub->children, i));
+	}
+}
+
+/* XXX This function and serialized_pubsub_on_rx_refresh are nearly identical */
+static int serialized_pubsub_on_server_timeout(void *userdata)
+{
+	struct sip_subscription_tree *sub_tree = userdata;
+	pjsip_dialog *dlg = sub_tree->dlg;
+
+	pjsip_dlg_inc_lock(dlg);
+	if (!sub_tree->evsub) {
+		pjsip_dlg_dec_lock(dlg);
+		return 0;
+	}
+	set_state_terminated(sub_tree->root);
+	send_notify(sub_tree, 1);
+	ast_test_suite_event_notify("SUBSCRIPTION_TERMINATED",
+			"Resource: %s",
+			sub_tree->root->resource);
+
+	pjsip_dlg_dec_lock(dlg);
+	ao2_cleanup(sub_tree);
+	return 0;
+}
+
+/*!
+ * \brief PJSIP callback when underlying SIP subscription changes state
+ *
+ * This callback is a bit of a mess, because it's not always called when
+ * you might expect it to be, and it can be called multiple times for the
+ * same state.
+ *
+ * For instance, this function is not called at all when an incoming SUBSCRIBE
+ * arrives to refresh a subscription. That makes sense in a way, since the
+ * subscription state has not made a change; it was active and remains active.
+ *
+ * However, if an incoming SUBSCRIBE arrives to end a subscription, then this
+ * will be called into once upon receiving the SUBSCRIBE (after the call to
+ * pubsub_on_rx_refresh) and again when sending a NOTIFY to end the subscription.
+ * In both cases, the apparent state of the subscription is "terminated".
+ *
+ * However, the double-terminated state changes don't happen in all cases. For
+ * instance, if a subscription expires, then the only time this callback is
+ * called is when we send the NOTIFY to end the subscription.
+ *
+ * As far as state changes are concerned, we only ever care about transitions
+ * to the "terminated" state. The action we take here is dependent on the
+ * conditions behind why the state change to "terminated" occurred. If the
+ * state change has occurred because we are sending a NOTIFY to end the
+ * subscription, we consider this to be the final hurrah of the subscription
+ * and take measures to start shutting things down. If the state change to
+ * terminated occurs for a different reason (e.g. transaction timeout,
+ * incoming SUBSCRIBE to end the subscription), then we push a task to
+ * send out a NOTIFY. When that NOTIFY is sent, this callback will be
+ * called again and we will actually shut down the subscription. The
+ * subscription tree's last_notify field let's us know if this is being
+ * called as a result of a terminating NOTIFY or not.
+ *
+ * There is no guarantee that this function will be called from a serializer
+ * thread since it can be called due to a transaction timeout. Therefore
+ * synchronization primitives are necessary to ensure that no operations
+ * step on each others' toes. The dialog lock is always held when this
+ * callback is called, so we ensure that relevant structures that may
+ * be touched in this function are always protected by the dialog lock
+ * elsewhere as well. The dialog lock in particular protects
+ *
+ * \li The subscription tree's last_notify field
+ * \li The subscription tree's evsub pointer
+ */
 static void pubsub_on_evsub_state(pjsip_evsub *evsub, pjsip_event *event)
 {
 	struct sip_subscription_tree *sub_tree;
 
+	ast_debug(3, "on_evsub_state called with state %s\n", pjsip_evsub_get_state_name(evsub));
+
 	if (pjsip_evsub_get_state(evsub) != PJSIP_EVSUB_STATE_TERMINATED) {
 		return;
 	}
@@ -3096,21 +3281,63 @@ static void pubsub_on_evsub_state(pjsip_evsub *evsub, pjsip_event *event)
 		return;
 	}
 
-	ao2_cleanup(sub_tree);
+	if (!sub_tree->last_notify) {
+		if (ast_sip_push_task(sub_tree->serializer, serialized_pubsub_on_server_timeout, ao2_bump(sub_tree))) {
+			ast_log(LOG_ERROR, "Failed to push task to send final NOTIFY.\n");
+			ao2_ref(sub_tree, -1);
+		} else {
+			return;
+		}
+	}
 
 	pjsip_evsub_set_mod_data(evsub, pubsub_module.id, NULL);
+	sub_tree->evsub = NULL;
+	ast_sip_dialog_set_serializer(sub_tree->dlg, NULL);
+	ast_sip_dialog_set_endpoint(sub_tree->dlg, NULL);
+	subscription_persistence_remove(sub_tree);
+	shutdown_subscriptions(sub_tree->root);
+
+	/* Remove evsub's reference to the sub_tree */
+	ao2_ref(sub_tree, -1);
 }
 
-static void set_state_terminated(struct ast_sip_subscription *sub)
+static int serialized_pubsub_on_rx_refresh(void *userdata)
 {
-	int i;
+	struct sip_subscription_tree *sub_tree = userdata;
+	pjsip_dialog *dlg = sub_tree->dlg;
 
-	sub->subscription_state = PJSIP_EVSUB_STATE_TERMINATED;
-	for (i = 0; i < AST_VECTOR_SIZE(&sub->children); ++i) {
-		set_state_terminated(AST_VECTOR_GET(&sub->children, i));
+	pjsip_dlg_inc_lock(dlg);
+	if (!sub_tree->evsub) {
+		pjsip_dlg_dec_lock(dlg);
+		return 0;
 	}
+
+	if (pjsip_evsub_get_state(sub_tree->evsub) == PJSIP_EVSUB_STATE_TERMINATED) {
+		set_state_terminated(sub_tree->root);
+	}
+
+	send_notify(sub_tree, 1);
+
+	ast_test_suite_event_notify(sub_tree->root->subscription_state == PJSIP_EVSUB_STATE_TERMINATED ?
+			"SUBSCRIPTION_TERMINATED" : "SUBSCRIPTION_REFRESHED",
+			"Resource: %s", sub_tree->root->resource);
+
+	pjsip_dlg_dec_lock(dlg);
+	ao2_cleanup(sub_tree);
+	return 0;
 }
 
+/*!
+ * \brief Called whenever an in-dialog SUBSCRIBE is received
+ *
+ * This includes both SUBSCRIBE requests that actually refresh the subscription
+ * as well as SUBSCRIBE requests that end the subscription.
+ *
+ * In the case where the SUBSCRIBE is actually refreshing the subscription we
+ * push a task to send an appropriate NOTIFY request. In the case where the
+ * SUBSCRIBE is ending the subscription, we let the pubsub_on_evsub_state
+ * callback take care of sending the terminal NOTIFY request instead.
+ */
 static void pubsub_on_rx_refresh(pjsip_evsub *evsub, pjsip_rx_data *rdata,
 		int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body)
 {
@@ -3121,31 +3348,19 @@ static void pubsub_on_rx_refresh(pjsip_evsub *evsub, pjsip_rx_data *rdata,
 		return;
 	}
 
-	/* If sending a NOTIFY to terminate a subscription, then pubsub_on_evsub_state()
-	 * will be called when we send the NOTIFY, and that will result in dropping the
-	 * refcount of sub_tree by one, and possibly destroying the sub_tree. We need to
-	 * hold a reference to the sub_tree until this function returns so that we don't
-	 * try to read from or write to freed memory by accident
+	/* PJSIP will set the evsub's state to terminated before calling into this function
+	 * if the Expires value of the incoming SUBSCRIBE is 0.
 	 */
-	ao2_ref(sub_tree, +1);
-
-	if (pjsip_evsub_get_state(evsub) == PJSIP_EVSUB_STATE_TERMINATED) {
-		set_state_terminated(sub_tree->root);
-	}
-
-	if (send_notify(sub_tree, 1)) {
-		*p_st_code = 500;
+	if (pjsip_evsub_get_state(sub_tree->evsub) != PJSIP_EVSUB_STATE_TERMINATED) {
+		if (ast_sip_push_task(sub_tree->serializer, serialized_pubsub_on_rx_refresh, ao2_bump(sub_tree))) {
+			/* If we can't push the NOTIFY refreshing task...we'll just go with it. */
+			ao2_ref(sub_tree, -1);
+		}
 	}
 
-	ast_test_suite_event_notify(sub_tree->root->subscription_state == PJSIP_EVSUB_STATE_TERMINATED ?
-			"SUBSCRIPTION_TERMINATED" : "SUBSCRIPTION_REFRESHED",
-			"Resource: %s", sub_tree->root->resource);
-
 	if (sub_tree->is_list) {
 		pj_list_insert_before(res_hdr, create_require_eventlist(rdata->tp_info.pool));
 	}
-
-	ao2_ref(sub_tree, -1);
 }
 
 static void pubsub_on_rx_notify(pjsip_evsub *evsub, pjsip_rx_data *rdata, int *p_st_code,
@@ -3170,7 +3385,6 @@ static int serialized_pubsub_on_client_refresh(void *userdata)
 		pjsip_evsub_send_request(sub_tree->evsub, tdata);
 	} else {
 		pjsip_evsub_terminate(sub_tree->evsub, PJ_TRUE);
-		return 0;
 	}
 	ao2_cleanup(sub_tree);
 	return 0;
@@ -3184,31 +3398,24 @@ static void pubsub_on_client_refresh(pjsip_evsub *evsub)
 	ast_sip_push_task(sub_tree->serializer, serialized_pubsub_on_client_refresh, sub_tree);
 }
 
-static int serialized_pubsub_on_server_timeout(void *userdata)
-{
-	struct sip_subscription_tree *sub_tree = userdata;
-
-	set_state_terminated(sub_tree->root);
-	send_notify(sub_tree, 1);
-	ast_test_suite_event_notify("SUBSCRIPTION_TERMINATED",
-			"Resource: %s",
-			sub_tree->root->resource);
-
-	ao2_cleanup(sub_tree);
-	return 0;
-}
-
 static void pubsub_on_server_timeout(pjsip_evsub *evsub)
 {
-	struct sip_subscription_tree *sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
 
+	struct sip_subscription_tree *sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
 	if (!sub_tree) {
-		/* if a subscription has been terminated and the subscription
-		   timeout/expires is less than the time it takes for all pending
-		   transactions to end then the subscription timer will not have
-		   been canceled yet and sub will be null, so do nothing since
-		   the subscription has already been terminated. */
-		return;
+		/* PJSIP does not terminate the server timeout timer when a SUBSCRIBE
+		 * with Expires: 0 arrives to end a subscription, nor does it terminate
+		 * this timer when we send a NOTIFY request in response to receiving such
+		 * a SUBSCRIBE. PJSIP does not stop the server timeout timer until the
+		 * NOTIFY transaction has finished (either through receiving a response
+		 * or through a transaction timeout).
+		 *
+		 * Therefore, it is possible that we can be told that a server timeout
+		 * occurred after we already thought that the subscription had been
+		 * terminated. In such a case, we will have already removed the sub_tree
+		 * from the evsub's mod_data array.
+		 */
+        return;
 	}
 
 	ao2_ref(sub_tree, +1);
@@ -3219,15 +3426,16 @@ static int ami_subscription_detail(struct sip_subscription_tree *sub_tree,
 				   struct ast_sip_ami *ami,
 				   const char *event)
 {
-	RAII_VAR(struct ast_str *, buf,
-		 ast_sip_create_ami_event(event, ami), ast_free);
+	struct ast_str *buf;
 
+	buf = ast_sip_create_ami_event(event, ami);
 	if (!buf) {
 		return -1;
 	}
 
 	sip_subscription_to_ami(sub_tree, &buf);
 	astman_append(ami->s, "%s\r\n", ast_str_buffer(buf));
+	ast_free(buf);
 	return 0;
 }
 
@@ -3253,12 +3461,8 @@ static int ami_show_subscriptions_inbound(struct mansession *s, const struct mes
 
 	num = for_each_subscription(ami_subscription_detail_inbound, &ami);
 
-	astman_append(s, "Event: InboundSubscriptionDetailComplete\r\n");
-	if (!ast_strlen_zero(ami.action_id)) {
-		astman_append(s, "ActionID: %s\r\n", ami.action_id);
-	}
-	astman_append(s, "EventList: Complete\r\n"
-		      "ListItems: %d\r\n\r\n", num);
+	astman_send_list_complete_start(s, m, "InboundSubscriptionDetailComplete", num);
+	astman_send_list_complete_end(s);
 	return 0;
 }
 
@@ -3272,12 +3476,8 @@ static int ami_show_subscriptions_outbound(struct mansession *s, const struct me
 
 	num = for_each_subscription(ami_subscription_detail_outbound, &ami);
 
-	astman_append(s, "Event: OutboundSubscriptionDetailComplete\r\n");
-	if (!ast_strlen_zero(ami.action_id)) {
-		astman_append(s, "ActionID: %s\r\n", ami.action_id);
-	}
-	astman_append(s, "EventList: Complete\r\n"
-		      "ListItems: %d\r\n\r\n", num);
+	astman_send_list_complete_start(s, m, "OutboundSubscriptionDetailComplete", num);
+	astman_send_list_complete_end(s);
 	return 0;
 }
 
@@ -3304,7 +3504,7 @@ static int format_ami_resource_lists(void *obj, void *arg, int flags)
 
 static int ami_show_resource_lists(struct mansession *s, const struct message *m)
 {
-	struct ast_sip_ami ami = { .s = s, .m = m };
+	struct ast_sip_ami ami = { .s = s, .m = m, .action_id = astman_get_header(m, "ActionID"), };
 	int num;
 	struct ao2_container *lists;
 
@@ -3321,10 +3521,8 @@ static int ami_show_resource_lists(struct mansession *s, const struct message *m
 
 	ao2_callback(lists, OBJ_NODATA, format_ami_resource_lists, &ami);
 
-	astman_append(s,
-		      "Event: ResourceListDetailComplete\r\n"
-		      "EventList: Complete\r\n"
-		      "ListItems: %d\r\n\r\n", num);
+	astman_send_list_complete_start(s, m, "ResourceListDetailComplete", num);
+	astman_send_list_complete_end(s);
 	return 0;
 }
 
@@ -4238,6 +4436,7 @@ static int load_module(void)
 	if (apply_list_configuration(sorcery)) {
 		ast_sip_unregister_service(&pubsub_module);
 		ast_sched_context_destroy(sched);
+		return AST_MODULE_LOAD_FAILURE;
 	}
 
 	ast_sorcery_apply_default(sorcery, "inbound-publication", "config", "pjsip.conf,criteria=type=inbound-publication");
@@ -4284,6 +4483,7 @@ static int unload_module(void)
 	ast_manager_unregister(AMI_SHOW_SUBSCRIPTIONS_INBOUND);
 	ast_manager_unregister("PJSIPShowResourceLists");
 
+	ast_sip_unregister_service(&pubsub_module);
 	if (sched) {
 		ast_sched_context_destroy(sched);
 	}
diff --git a/res/res_pjsip_pubsub.exports.in b/res/res_pjsip_pubsub.exports.in
index 2a6b75f..6616524 100644
--- a/res/res_pjsip_pubsub.exports.in
+++ b/res/res_pjsip_pubsub.exports.in
@@ -37,6 +37,8 @@
 		LINKER_SYMBOL_PREFIXast_sip_subscription_get_local_uri;
 		LINKER_SYMBOL_PREFIXast_sip_subscription_get_remote_uri;
 		LINKER_SYMBOL_PREFIXast_sip_subscription_get_header;
+		LINKER_SYMBOL_PREFIXast_sip_subscription_is_terminated;
+		LINKER_SYMBOL_PREFIXast_sip_subscription_destroy;
 	local:
 		*;
 };
diff --git a/res/res_pjsip_refer.c b/res/res_pjsip_refer.c
index 1cbbbc0..4896a00 100644
--- a/res/res_pjsip_refer.c
+++ b/res/res_pjsip_refer.c
@@ -37,6 +37,7 @@
 #include "asterisk/framehook.h"
 #include "asterisk/stasis_bridges.h"
 #include "asterisk/stasis_channels.h"
+#include "asterisk/causes.h"
 
 /*! \brief REFER Progress structure */
 struct refer_progress {
@@ -242,16 +243,16 @@ static struct ast_frame *refer_progress_framehook(struct ast_channel *chan, stru
 
 	/* If a notification is due to be sent push it to the thread pool */
 	if (notification) {
-		if (ast_sip_push_task(progress->serializer, refer_progress_notify, notification)) {
-			ao2_cleanup(notification);
-		}
-
 		/* If the subscription is being terminated we don't need the frame hook any longer */
 		if (notification->state == PJSIP_EVSUB_STATE_TERMINATED) {
 			ast_debug(3, "Detaching REFER progress monitoring hook from '%s' as subscription is being terminated\n",
 				ast_channel_name(chan));
 			ast_framehook_detach(chan, progress->framehook);
 		}
+
+		if (ast_sip_push_task(progress->serializer, refer_progress_notify, notification)) {
+			ao2_cleanup(notification);
+		}
 	}
 
 	return f;
@@ -407,7 +408,7 @@ struct refer_attended {
 	/*! \brief Transferer channel */
 	struct ast_channel *transferer_chan;
 	/*! \brief Second transferer session */
-	struct ast_sip_session *transferer_second	;
+	struct ast_sip_session *transferer_second;
 	/*! \brief Optional refer progress structure */
 	struct refer_progress *progress;
 };
@@ -418,16 +419,20 @@ static void refer_attended_destroy(void *obj)
 	struct refer_attended *attended = obj;
 
 	ao2_cleanup(attended->transferer);
-	ast_channel_unref(attended->transferer_chan);
+	ast_channel_cleanup(attended->transferer_chan);
 	ao2_cleanup(attended->transferer_second);
+	ao2_cleanup(attended->progress);
 }
 
 /*! \brief Allocator for attended transfer task */
-static struct refer_attended *refer_attended_alloc(struct ast_sip_session *transferer, struct ast_sip_session *transferer_second,
+static struct refer_attended *refer_attended_alloc(struct ast_sip_session *transferer,
+	struct ast_sip_session *transferer_second,
 	struct refer_progress *progress)
 {
-	struct refer_attended *attended = ao2_alloc(sizeof(*attended), refer_attended_destroy);
+	struct refer_attended *attended;
 
+	attended = ao2_alloc_options(sizeof(*attended), refer_attended_destroy,
+		AO2_ALLOC_OPT_LOCK_NOLOCK);
 	if (!attended) {
 		return NULL;
 	}
@@ -447,20 +452,30 @@ static struct refer_attended *refer_attended_alloc(struct ast_sip_session *trans
 	return attended;
 }
 
-/*! \brief Task for attended transfer */
-static int refer_attended(void *data)
+static int defer_termination_cancel(void *data)
 {
-	RAII_VAR(struct refer_attended *, attended, data, ao2_cleanup);
-	int response = 0;
+	struct ast_sip_session *session = data;
 
-	if (!attended->transferer_second->channel) {
-		return -1;
-	}
+	ast_sip_session_defer_termination_cancel(session);
+	ao2_ref(session, -1);
+	return 0;
+}
 
-	ast_debug(3, "Performing a REFER attended transfer - Transferer #1: %s Transferer #2: %s\n",
-		ast_channel_name(attended->transferer_chan), ast_channel_name(attended->transferer_second->channel));
+/*!
+ * \internal
+ * \brief Convert transfer enum to SIP response code.
+ * \since 13.3.0
+ *
+ * \param xfer_code Core transfer function enum result.
+ *
+ * \return SIP response code
+ */
+static int xfer_response_code2sip(enum ast_transfer_result xfer_code)
+{
+	int response;
 
-	switch (ast_bridge_transfer_attended(attended->transferer_chan, attended->transferer_second->channel)) {
+	response = 503;
+	switch (xfer_code) {
 	case AST_BRIDGE_TRANSFER_INVALID:
 		response = 400;
 		break;
@@ -472,21 +487,55 @@ static int refer_attended(void *data)
 		break;
 	case AST_BRIDGE_TRANSFER_SUCCESS:
 		response = 200;
-		ast_sip_session_defer_termination(attended->transferer);
 		break;
 	}
+	return response;
+}
+
+/*! \brief Task for attended transfer executed by attended->transferer_second serializer */
+static int refer_attended_task(void *data)
+{
+	struct refer_attended *attended = data;
+	int response;
+
+	if (attended->transferer_second->channel) {
+		ast_debug(3, "Performing a REFER attended transfer - Transferer #1: %s Transferer #2: %s\n",
+			ast_channel_name(attended->transferer_chan),
+			ast_channel_name(attended->transferer_second->channel));
+
+		response = xfer_response_code2sip(ast_bridge_transfer_attended(
+			attended->transferer_chan,
+			attended->transferer_second->channel));
 
-	ast_debug(3, "Final response for REFER attended transfer - Transferer #1: %s Transferer #2: %s is '%d'\n",
-		ast_channel_name(attended->transferer_chan), ast_channel_name(attended->transferer_second->channel), response);
+		ast_debug(3, "Final response for REFER attended transfer - Transferer #1: %s Transferer #2: %s is '%d'\n",
+			ast_channel_name(attended->transferer_chan),
+			ast_channel_name(attended->transferer_second->channel),
+			response);
+	} else {
+		ast_debug(3, "Received REFER request on channel '%s' but other channel has gone.\n",
+			ast_channel_name(attended->transferer_chan));
+		response = 603;
+	}
 
-	if (attended->progress && response) {
-		struct refer_progress_notification *notification = refer_progress_notification_alloc(attended->progress, response, PJSIP_EVSUB_STATE_TERMINATED);
+	if (attended->progress) {
+		struct refer_progress_notification *notification;
 
+		notification = refer_progress_notification_alloc(attended->progress, response,
+			PJSIP_EVSUB_STATE_TERMINATED);
 		if (notification) {
 			refer_progress_notify(notification);
 		}
 	}
 
+	if (response != 200) {
+		if (!ast_sip_push_task(attended->transferer->serializer,
+			defer_termination_cancel, attended->transferer)) {
+			/* Gave the ref to the pushed task. */
+			attended->transferer = NULL;
+		}
+	}
+
+	ao2_ref(attended, -1);
 	return 0;
 }
 
@@ -599,9 +648,16 @@ static void refer_blind_callback(struct ast_channel *chan, struct transfer_chann
 
 	if (refer->replaces) {
 		char replaces[512];
-
-		pjsip_hdr_print_on(refer->replaces, replaces, sizeof(replaces));
-		pbx_builtin_setvar_helper(chan, "__SIPREPLACESHDR", S_OR(replaces, NULL));
+		char *replaces_val = NULL;
+		int len;
+
+		len = pjsip_hdr_print_on(refer->replaces, replaces, sizeof(replaces) - 1);
+		if (len != -1) {
+			/* pjsip_hdr_print_on does not NULL terminate the buffer */
+			replaces[len] = '\0';
+			replaces_val = replaces + sizeof("Replaces:");
+		}
+		pbx_builtin_setvar_helper(chan, "__SIPREPLACESHDR", replaces_val);
 	} else {
 		pbx_builtin_setvar_helper(chan, "SIPREPLACESHDR", NULL);
 	}
@@ -616,6 +672,26 @@ static void refer_blind_callback(struct ast_channel *chan, struct transfer_chann
 	}
 }
 
+/*!
+ * \internal
+ * \brief Set the passed in context variable to the determined transfer context.
+ * \since 13.3.0
+ *
+ * \param context Set to the determined transfer context.
+ * \param session INVITE dialog SIP session.
+ */
+#define DETERMINE_TRANSFER_CONTEXT(context, session)									\
+	do {																				\
+		ast_channel_lock((session)->channel);											\
+		context = pbx_builtin_getvar_helper((session)->channel, "TRANSFER_CONTEXT");	\
+		if (ast_strlen_zero(context)) {													\
+			context = (session)->endpoint->context;										\
+		} else {																		\
+			context = ast_strdupa(context);												\
+		}																				\
+		ast_channel_unlock((session)->channel);											\
+	} while (0)																			\
+
 static int refer_incoming_attended_request(struct ast_sip_session *session, pjsip_rx_data *rdata, pjsip_sip_uri *target_uri,
 	pjsip_param *replaces_param, struct refer_progress *progress)
 {
@@ -655,8 +731,16 @@ static int refer_incoming_attended_request(struct ast_sip_session *session, pjsi
 			return 500;
 		}
 
+		if (ast_sip_session_defer_termination(session)) {
+			ast_log(LOG_ERROR, "Received REFER request on channel '%s' from endpoint '%s' for local dialog but could not defer termination, rejecting\n",
+				ast_channel_name(session->channel), ast_sorcery_object_get_id(session->endpoint));
+			ao2_cleanup(attended);
+			return 500;
+		}
+
 		/* Push it to the other session, which will have both channels with minimal locking */
-		if (ast_sip_push_task(other_session->serializer, refer_attended, attended)) {
+		if (ast_sip_push_task(other_session->serializer, refer_attended_task, attended)) {
+			ast_sip_session_defer_termination_cancel(session);
 			ao2_cleanup(attended);
 			return 500;
 		}
@@ -666,16 +750,15 @@ static int refer_incoming_attended_request(struct ast_sip_session *session, pjsi
 
 		return 200;
 	} else {
-		const char *context = (session->channel ? pbx_builtin_getvar_helper(session->channel, "TRANSFER_CONTEXT") : "");
+		const char *context;
 		struct refer_blind refer = { 0, };
+		int response;
 
-		if (ast_strlen_zero(context)) {
-			context = session->endpoint->context;
-		}
+		DETERMINE_TRANSFER_CONTEXT(context, session);
 
 		if (!ast_exists_extension(NULL, context, "external_replaces", 1, NULL)) {
-			ast_log(LOG_ERROR, "Received REFER for remote session on channel '%s' from endpoint '%s' but 'external_replaces' context does not exist for handling\n",
-				ast_channel_name(session->channel), ast_sorcery_object_get_id(session->endpoint));
+			ast_log(LOG_ERROR, "Received REFER for remote session on channel '%s' from endpoint '%s' but 'external_replaces' extension not found in context %s\n",
+				ast_channel_name(session->channel), ast_sorcery_object_get_id(session->endpoint), context);
 			return 404;
 		}
 
@@ -685,22 +768,20 @@ static int refer_incoming_attended_request(struct ast_sip_session *session, pjsi
 		refer.replaces = replaces;
 		refer.refer_to = target_uri;
 
-		switch (ast_bridge_transfer_blind(1, session->channel, "external_replaces", context, refer_blind_callback, &refer)) {
-		case AST_BRIDGE_TRANSFER_INVALID:
-			return 400;
-		case AST_BRIDGE_TRANSFER_NOT_PERMITTED:
-			return 403;
-		case AST_BRIDGE_TRANSFER_FAIL:
+		if (ast_sip_session_defer_termination(session)) {
+			ast_log(LOG_ERROR, "Received REFER for remote session on channel '%s' from endpoint '%s' but could not defer termination, rejecting\n",
+				ast_channel_name(session->channel),
+				ast_sorcery_object_get_id(session->endpoint));
 			return 500;
-		case AST_BRIDGE_TRANSFER_SUCCESS:
-			ast_sip_session_defer_termination(session);
-			return 200;
 		}
 
-		return 503;
+		response = xfer_response_code2sip(ast_bridge_transfer_blind(1, session->channel,
+			"external_replaces", context, refer_blind_callback, &refer));
+		if (response != 200) {
+			ast_sip_session_defer_termination_cancel(session);
+		}
+		return response;
 	}
-
-	return 0;
 }
 
 static int refer_incoming_blind_request(struct ast_sip_session *session, pjsip_rx_data *rdata, pjsip_sip_uri *target,
@@ -709,16 +790,10 @@ static int refer_incoming_blind_request(struct ast_sip_session *session, pjsip_r
 	const char *context;
 	char exten[AST_MAX_EXTENSION];
 	struct refer_blind refer = { 0, };
-
-	if (!session->channel) {
-		return 404;
-	}
+	int response;
 
 	/* If no explicit transfer context has been provided use their configured context */
-	context = pbx_builtin_getvar_helper(session->channel, "TRANSFER_CONTEXT");
-	if (ast_strlen_zero(context)) {
-		context = session->endpoint->context;
-	}
+	DETERMINE_TRANSFER_CONTEXT(context, session);
 
 	/* Using the user portion of the target URI see if it exists as a valid extension in their context */
 	ast_copy_pj_str(exten, &target->user, sizeof(exten));
@@ -733,19 +808,19 @@ static int refer_incoming_blind_request(struct ast_sip_session *session, pjsip_r
 	refer.rdata = rdata;
 	refer.refer_to = target;
 
-	switch (ast_bridge_transfer_blind(1, session->channel, exten, context, refer_blind_callback, &refer)) {
-	case AST_BRIDGE_TRANSFER_INVALID:
-		return 400;
-	case AST_BRIDGE_TRANSFER_NOT_PERMITTED:
-		return 403;
-	case AST_BRIDGE_TRANSFER_FAIL:
+	if (ast_sip_session_defer_termination(session)) {
+		ast_log(LOG_ERROR, "Channel '%s' from endpoint '%s' attempted blind transfer but could not defer termination, rejecting\n",
+			ast_channel_name(session->channel),
+			ast_sorcery_object_get_id(session->endpoint));
 		return 500;
-	case AST_BRIDGE_TRANSFER_SUCCESS:
-		ast_sip_session_defer_termination(session);
-		return 200;
 	}
 
-	return 503;
+	response = xfer_response_code2sip(ast_bridge_transfer_blind(1, session->channel,
+		exten, context, refer_blind_callback, &refer));
+	if (response != 200) {
+		ast_sip_session_defer_termination_cancel(session);
+	}
+	return response;
 }
 
 /*! \brief Structure used to retrieve channel from another session */
@@ -788,8 +863,9 @@ static int refer_incoming_invite_request(struct ast_sip_session *session, struct
 	/* If a Replaces header is present make sure it is valid */
 	if (pjsip_replaces_verify_request(rdata, &other_dlg, PJ_TRUE, &packet) != PJ_SUCCESS) {
 		response = packet->msg->line.status.code;
+		ast_assert(response != 0);
 		pjsip_tx_data_dec_ref(packet);
-		goto end;
+		goto inv_replace_failed;
 	}
 
 	/* If no other dialog exists then this INVITE request does not have a Replaces header */
@@ -803,21 +879,21 @@ static int refer_incoming_invite_request(struct ast_sip_session *session, struct
 	/* Don't accept an in-dialog INVITE with Replaces as it does not make much sense */
 	if (session->inv_session->dlg->state == PJSIP_DIALOG_STATE_ESTABLISHED) {
 		response = 488;
-		goto end;
+		goto inv_replace_failed;
 	}
 
 	if (!other_session) {
-		response = 481;
 		ast_debug(3, "INVITE with Replaces received on channel '%s' from endpoint '%s', but requested session does not exist\n",
 			ast_channel_name(session->channel), ast_sorcery_object_get_id(session->endpoint));
-		goto end;
+		response = 481;
+		goto inv_replace_failed;
 	}
 
 	invite.session = other_session;
 
 	if (ast_sip_push_task_synchronous(other_session->serializer, invite_replaces, &invite)) {
 		response = 481;
-		goto end;
+		goto inv_replace_failed;
 	}
 
 	ast_channel_lock(session->channel);
@@ -825,48 +901,69 @@ static int refer_incoming_invite_request(struct ast_sip_session *session, struct
 	ast_channel_unlock(session->channel);
 	ast_raw_answer(session->channel);
 
+	ast_debug(3, "INVITE with Replaces being attempted.  '%s' --> '%s'\n",
+		ast_channel_name(session->channel), ast_channel_name(invite.channel));
+
 	if (!invite.bridge) {
 		struct ast_channel *chan = session->channel;
 
-		/* This will use a synchronous task but we aren't operating in the serializer at this point in time, so it
-		 * won't deadlock */
-		if (!ast_channel_move(invite.channel, session->channel)) {
+		/*
+		 * This will use a synchronous task but we aren't operating in
+		 * the serializer at this point in time, so it won't deadlock.
+		 */
+		if (!ast_channel_move(invite.channel, chan)) {
+			/*
+			 * We can't directly use session->channel because ast_channel_move()
+			 * does a masquerade which changes session->channel to a different
+			 * channel.  To ensure we work on the right channel we store a
+			 * pointer locally before we begin so it remains valid.
+			 */
 			ast_hangup(chan);
 		} else {
-			response = 500;
+			response = AST_CAUSE_FAILURE;
 		}
 	} else {
 		if (ast_bridge_impart(invite.bridge, session->channel, invite.channel, NULL,
 			AST_BRIDGE_IMPART_CHAN_INDEPENDENT)) {
-			response = 500;
+			response = AST_CAUSE_FAILURE;
 		}
 	}
 
+	ast_channel_unref(invite.channel);
+	ao2_cleanup(invite.bridge);
+
 	if (!response) {
-		ast_debug(3, "INVITE with Replaces successfully completed on channels '%s' and '%s'\n",
-			ast_channel_name(session->channel), ast_channel_name(invite.channel));
+		/*
+		 * On success we cannot use session->channel in the debug message.
+		 * This thread either no longer has a ref to session->channel or
+		 * session->channel is no longer the original channel.
+		 */
+		ast_debug(3, "INVITE with Replaces successfully completed.\n");
+	} else {
+		ast_debug(3, "INVITE with Replaces failed on channel '%s', hanging up with cause '%d'\n",
+			ast_channel_name(session->channel), response);
+		ast_channel_lock(session->channel);
+		ast_channel_hangupcause_set(session->channel, response);
+		ast_channel_unlock(session->channel);
+		ast_hangup(session->channel);
 	}
 
-	ast_channel_unref(invite.channel);
-	ao2_cleanup(invite.bridge);
+	return 1;
 
-end:
-	if (response) {
-		if (session->inv_session->dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED) {
-			ast_debug(3, "INVITE with Replaces failed on channel '%s', sending response of '%d'\n",
-				ast_channel_name(session->channel), response);
-			session->defer_terminate = 1;
-			ast_hangup(session->channel);
-			session->channel = NULL;
-
-			if (pjsip_inv_end_session(session->inv_session, response, NULL, &packet) == PJ_SUCCESS) {
-				ast_sip_session_send_response(session, packet);
-			}
-		} else {
-			ast_debug(3, "INVITE with Replaces in-dialog on channel '%s', hanging up\n",
-				ast_channel_name(session->channel));
-			ast_queue_hangup(session->channel);
+inv_replace_failed:
+	if (session->inv_session->dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED) {
+		ast_debug(3, "INVITE with Replaces failed on channel '%s', sending response of '%d'\n",
+			ast_channel_name(session->channel), response);
+		session->defer_terminate = 1;
+		ast_hangup(session->channel);
+
+		if (pjsip_inv_end_session(session->inv_session, response, NULL, &packet) == PJ_SUCCESS) {
+			ast_sip_session_send_response(session, packet);
 		}
+	} else {
+		ast_debug(3, "INVITE with Replaces in-dialog on channel '%s', hanging up\n",
+			ast_channel_name(session->channel));
+		ast_queue_hangup(session->channel);
 	}
 
 	return 1;
@@ -885,6 +982,14 @@ static int refer_incoming_refer_request(struct ast_sip_session *session, struct
 	static const pj_str_t str_refer_to = { "Refer-To", 8 };
 	static const pj_str_t str_replaces = { "Replaces", 8 };
 
+	if (!session->channel) {
+		/* No channel to refer.  Likely because the call was just hung up. */
+		pjsip_dlg_respond(session->inv_session->dlg, rdata, 404, NULL, NULL, NULL);
+		ast_debug(3, "Received a REFER on a session with no channel from endpoint '%s'.\n",
+			ast_sorcery_object_get_id(session->endpoint));
+		return 0;
+	}
+
 	if (!session->endpoint->allowtransfer) {
 		pjsip_dlg_respond(session->inv_session->dlg, rdata, 603, NULL, NULL, NULL);
 		ast_log(LOG_WARNING, "Endpoint %s transfer attempt blocked due to configuration\n",
@@ -982,10 +1087,41 @@ static int refer_incoming_request(struct ast_sip_session *session, pjsip_rx_data
 	}
 }
 
-static void refer_outgoing_request(struct ast_sip_session *session, struct pjsip_tx_data *tdata)
+/*!
+ * \brief Use the value of a channel variable as the value of a SIP header
+ *
+ * This looks up a variable name on a channel, then takes that value and adds
+ * it to an outgoing SIP request. If the header already exists on the message,
+ * then no action is taken.
+ *
+ * \pre chan is locked.
+ *
+ * \param chan The channel on which to find the variable.
+ * \param var_name The name of the channel variable to use.
+ * \param header_name The name of the SIP header to add to the outgoing message.
+ * \param tdata The outgoing SIP message on which to add the header
+ */
+static void add_header_from_channel_var(struct ast_channel *chan, const char *var_name, const char *header_name, pjsip_tx_data *tdata)
 {
-	const char *hdr;
+	const char *var_value;
+	pj_str_t pj_header_name;
+	pjsip_hdr *header;
 
+	var_value = pbx_builtin_getvar_helper(chan, var_name);
+	if (ast_strlen_zero(var_value)) {
+		return;
+	}
+
+	pj_cstr(&pj_header_name, header_name);
+	header = pjsip_msg_find_hdr_by_name(tdata->msg, &pj_header_name, NULL);
+	if (header) {
+		return;
+	}
+	ast_sip_add_header(tdata, header_name, var_value);
+}
+
+static void refer_outgoing_request(struct ast_sip_session *session, struct pjsip_tx_data *tdata)
+{
 	if (pjsip_method_cmp(&tdata->msg->line.req.method, &pjsip_invite_method)
 		|| !session->channel
 		|| session->inv_session->state != PJSIP_INV_STATE_NULL) {
@@ -993,15 +1129,8 @@ static void refer_outgoing_request(struct ast_sip_session *session, struct pjsip
 	}
 
 	ast_channel_lock(session->channel);
-	hdr = pbx_builtin_getvar_helper(session->channel, "SIPREPLACESHDR");
-	if (!ast_strlen_zero(hdr)) {
-		ast_sip_add_header(tdata, "Replaces", hdr);
-	}
-
-	hdr = pbx_builtin_getvar_helper(session->channel, "SIPREFERREDBYHDR");
-	if (!ast_strlen_zero(hdr)) {
-		ast_sip_add_header(tdata, "Referred-By", hdr);
-	}
+	add_header_from_channel_var(session->channel, "SIPREPLACESHDR", "Replaces", tdata);
+	add_header_from_channel_var(session->channel, "SIPREFERREDBYHDR", "Referred-By", tdata);
 	ast_channel_unlock(session->channel);
 }
 
diff --git a/res/res_pjsip_registrar.c b/res/res_pjsip_registrar.c
index aad3dd4..fed4393 100644
--- a/res/res_pjsip_registrar.c
+++ b/res/res_pjsip_registrar.c
@@ -57,7 +57,7 @@ static int registrar_get_expiration(const struct ast_sip_aor *aor, const pjsip_c
 	pjsip_expires_hdr *expires;
 	int expiration = aor->default_expiration;
 
-	if (contact->expires != -1) {
+	if (contact && contact->expires != -1) {
 		/* Expiration was provided with the contact itself */
 		expiration = contact->expires;
 	} else if ((expires = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL))) {
@@ -418,12 +418,12 @@ static int rx_task(void *data)
 	pjsip_contact_hdr *contact_hdr = NULL;
 	struct registrar_contact_details details = { 0, };
 	pjsip_tx_data *tdata;
-	pjsip_response_addr addr;
 	const char *aor_name = ast_sorcery_object_get_id(task_data->aor);
 	RAII_VAR(struct ast_str *, path_str, NULL, ast_free);
 	struct ast_sip_contact *response_contact;
 	char *user_agent = NULL;
 	pjsip_user_agent_hdr *user_agent_hdr;
+	pjsip_expires_hdr *expires_hdr;
 
 	/* Retrieve the current contacts, we'll need to know whether to update or not */
 	contacts = ast_sip_location_retrieve_aor_contacts(task_data->aor);
@@ -500,7 +500,7 @@ static int rx_task(void *data)
 
 			if (ast_sip_location_add_contact(task_data->aor, contact_uri, ast_tvadd(ast_tvnow(),
 				ast_samp2tv(expiration, 1)), path_str ? ast_str_buffer(path_str) : NULL,
-					user_agent)) {
+					user_agent, task_data->endpoint)) {
 				ast_log(LOG_ERROR, "Unable to bind contact '%s' to AOR '%s'\n",
 						contact_uri, aor_name);
 				continue;
@@ -597,12 +597,13 @@ static int rx_task(void *data)
 
 	ao2_callback(contacts, 0, registrar_add_contact, tdata);
 
-	if (pjsip_get_response_addr(tdata->pool, task_data->rdata, &addr) == PJ_SUCCESS) {
-		ast_sip_send_response(&addr, tdata, task_data->endpoint);
-	} else {
-		pjsip_tx_data_dec_ref(tdata);
+	if ((expires_hdr = pjsip_msg_find_hdr(task_data->rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL))) {
+		expires_hdr = pjsip_expires_hdr_create(tdata->pool, registrar_get_expiration(task_data->aor, NULL, task_data->rdata));
+		pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires_hdr);
 	}
 
+	ast_sip_send_stateful_response(task_data->rdata, tdata, task_data->endpoint);
+
 	return PJ_TRUE;
 }
 
@@ -766,17 +767,14 @@ static int ami_show_registrations(struct mansession *s, const struct message *m)
 {
 	int count = 0;
 	struct ast_sip_ami ami = { .s = s, .m = m, .arg = &count, .action_id = astman_get_header(m, "ActionID"), };
+
 	astman_send_listack(s, m, "Following are Events for each Inbound "
 			    "registration", "start");
 
 	ami_registrations_endpoints(&ami);
 
-	astman_append(s, "Event: InboundRegistrationDetailComplete\r\n");
-	if (!ast_strlen_zero(ami.action_id)) {
-		astman_append(s, "ActionID: %s\r\n", ami.action_id);
-	}
-	astman_append(s, "EventList: Complete\r\n"
-		      "ListItems: %d\r\n\r\n", count);
+	astman_send_list_complete_start(s, m, "InboundRegistrationDetailComplete", count);
+	astman_send_list_complete_end(s);
 	return 0;
 }
 
diff --git a/res/res_pjsip_registrar_expire.c b/res/res_pjsip_registrar_expire.c
index 256ed9e..5b2b350 100644
--- a/res/res_pjsip_registrar_expire.c
+++ b/res/res_pjsip_registrar_expire.c
@@ -57,32 +57,76 @@ static void contact_expiration_destroy(void *obj)
 /*! \brief Hashing function for contact auto-expiration */
 static int contact_expiration_hash(const void *obj, const int flags)
 {
-	const struct contact_expiration *expiration = obj;
-	const char *id = obj;
-
-	return ast_str_hash(flags & OBJ_KEY ? id : ast_sorcery_object_get_id(expiration->contact));
+	const struct contact_expiration *object;
+	const char *key;
+
+	switch (flags & OBJ_SEARCH_MASK) {
+	case OBJ_SEARCH_KEY:
+		key = obj;
+		break;
+	case OBJ_SEARCH_OBJECT:
+		object = obj;
+		key = ast_sorcery_object_get_id(object->contact);
+		break;
+	default:
+		/* Hash can only work on something with a full key. */
+		ast_assert(0);
+		return 0;
+	}
+	return ast_str_hash(key);
 }
 
 /*! \brief Comparison function for contact auto-expiration */
 static int contact_expiration_cmp(void *obj, void *arg, int flags)
 {
-	struct contact_expiration *expiration1 = obj, *expiration2 = arg;
-	const char *id = arg;
-
-	return !strcmp(ast_sorcery_object_get_id(expiration1->contact), flags & OBJ_KEY ? id :
-		       ast_sorcery_object_get_id(expiration2->contact)) ? CMP_MATCH | CMP_STOP : 0;
+	const struct contact_expiration *object_left = obj;
+	const struct contact_expiration *object_right = arg;
+	const char *right_key = arg;
+	int cmp;
+
+	switch (flags & OBJ_SEARCH_MASK) {
+	case OBJ_SEARCH_OBJECT:
+		right_key = ast_sorcery_object_get_id(object_right->contact);
+		/* Fall through */
+	case OBJ_SEARCH_KEY:
+		cmp = strcmp(ast_sorcery_object_get_id(object_left->contact), right_key);
+		break;
+	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.
+		 */
+		cmp = strncmp(ast_sorcery_object_get_id(object_left->contact), right_key,
+			strlen(right_key));
+		break;
+	default:
+		/*
+		 * What arg points to is specific to this traversal callback
+		 * and has no special meaning to astobj2.
+		 */
+		cmp = 0;
+		break;
+	}
+	if (cmp) {
+		return 0;
+	}
+	/*
+	 * At this point the traversal callback is identical to a sorted
+	 * container.
+	 */
+	return CMP_MATCH;
 }
 
 /*! \brief Scheduler function which deletes a contact */
 static int contact_expiration_expire(const void *data)
 {
-	RAII_VAR(struct contact_expiration *, expiration, (void*)data, ao2_cleanup);
+	struct contact_expiration *expiration = (void *) data;
 
 	expiration->sched = -1;
 
 	/* This will end up invoking the deleted observer callback, which will perform the unlinking and such */
 	ast_sorcery_delete(ast_sip_get_sorcery(), expiration->contact);
-
+	ao2_ref(expiration, -1);
 	return 0;
 }
 
@@ -90,14 +134,16 @@ static int contact_expiration_expire(const void *data)
 static void contact_expiration_observer_created(const void *object)
 {
 	const struct ast_sip_contact *contact = object;
-	RAII_VAR(struct contact_expiration *, expiration, NULL, ao2_cleanup);
+	struct contact_expiration *expiration;
 	int expires = MAX(0, ast_tvdiff_ms(contact->expiration_time, ast_tvnow()));
 
 	if (ast_tvzero(contact->expiration_time)) {
 		return;
 	}
 
-	if (!(expiration = ao2_alloc_options(sizeof(*expiration), contact_expiration_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK))) {
+	expiration = ao2_alloc_options(sizeof(*expiration), contact_expiration_destroy,
+		AO2_ALLOC_OPT_LOCK_NOLOCK);
+	if (!expiration) {
 		return;
 	}
 
@@ -106,39 +152,46 @@ static void contact_expiration_observer_created(const void *object)
 
 	ao2_ref(expiration, +1);
 	if ((expiration->sched = ast_sched_add(sched, expires, contact_expiration_expire, expiration)) < 0) {
-		ao2_cleanup(expiration);
+		ao2_ref(expiration, -1);
 		ast_log(LOG_ERROR, "Scheduled expiration for contact '%s' could not be performed, contact may persist past life\n",
 			ast_sorcery_object_get_id(contact));
-		return;
+	} else {
+		ao2_link(contact_autoexpire, expiration);
 	}
-
-	ao2_link(contact_autoexpire, expiration);
+	ao2_ref(expiration, -1);
 }
 
 /*! \brief Observer callback for when a contact is updated */
 static void contact_expiration_observer_updated(const void *object)
 {
 	const struct ast_sip_contact *contact = object;
-	RAII_VAR(struct contact_expiration *, expiration, ao2_find(contact_autoexpire, ast_sorcery_object_get_id(contact), OBJ_KEY), ao2_cleanup);
+	struct contact_expiration *expiration;
 	int expires = MAX(0, ast_tvdiff_ms(contact->expiration_time, ast_tvnow()));
 
+	expiration = ao2_find(contact_autoexpire, ast_sorcery_object_get_id(contact),
+		OBJ_SEARCH_KEY);
 	if (!expiration) {
 		return;
 	}
 
-	AST_SCHED_REPLACE_UNREF(expiration->sched, sched, expires, contact_expiration_expire, expiration, ao2_cleanup(expiration), ao2_cleanup(expiration), ao2_ref(expiration, +1));
+	AST_SCHED_REPLACE_UNREF(expiration->sched, sched, expires, contact_expiration_expire,
+		expiration, ao2_cleanup(expiration), ao2_cleanup(expiration), ao2_ref(expiration, +1));
+	ao2_ref(expiration, -1);
 }
 
 /*! \brief Observer callback for when a contact is deleted */
 static void contact_expiration_observer_deleted(const void *object)
 {
-	RAII_VAR(struct contact_expiration *, expiration, ao2_find(contact_autoexpire, ast_sorcery_object_get_id(object), OBJ_KEY | OBJ_UNLINK), ao2_cleanup);
+	struct contact_expiration *expiration;
 
+	expiration = ao2_find(contact_autoexpire, ast_sorcery_object_get_id(object),
+		OBJ_SEARCH_KEY | OBJ_UNLINK);
 	if (!expiration) {
 		return;
 	}
 
 	AST_SCHED_DEL_UNREF(sched, expiration->sched, ao2_cleanup(expiration));
+	ao2_ref(expiration, -1);
 }
 
 /*! \brief Observer callbacks for autoexpiring contacts */
@@ -166,65 +219,78 @@ static int contact_expiration_setup(void *obj, void *arg, int flags)
 /*! \brief Initialize auto-expiration of any existing contacts */
 static void contact_expiration_initialize_existing(void)
 {
-	RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
+	struct ao2_container *contacts;
 
-	if (!(contacts = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "contact", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL))) {
+	contacts = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "contact",
+		AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+	if (!contacts) {
 		return;
 	}
 
 	ao2_callback(contacts, OBJ_NODATA, contact_expiration_setup, NULL);
+	ao2_ref(contacts, -1);
+}
+
+static int unload_observer_delete(void *obj, void *arg, int flags)
+{
+	struct contact_expiration *expiration = obj;
+
+	AST_SCHED_DEL_UNREF(sched, expiration->sched, ao2_cleanup(expiration));
+	return CMP_MATCH;
+}
+
+static int unload_module(void)
+{
+	ast_sorcery_observer_remove(ast_sip_get_sorcery(), "contact", &contact_expiration_observer);
+	if (sched) {
+		ao2_callback(contact_autoexpire, OBJ_MULTIPLE | OBJ_NODATA | OBJ_UNLINK,
+			unload_observer_delete, NULL);
+		ast_sched_context_destroy(sched);
+		sched = NULL;
+	}
+	ao2_cleanup(contact_autoexpire);
+	contact_autoexpire = NULL;
+
+	return 0;
 }
 
 static int load_module(void)
 {
 	CHECK_PJSIP_MODULE_LOADED();
 
-	if (!(contact_autoexpire = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, CONTACT_AUTOEXPIRE_BUCKETS,
-		contact_expiration_hash, contact_expiration_cmp))) {
+	contact_autoexpire = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK,
+		CONTACT_AUTOEXPIRE_BUCKETS, contact_expiration_hash, contact_expiration_cmp);
+	if (!contact_autoexpire) {
 		ast_log(LOG_ERROR, "Could not create container for contact auto-expiration\n");
 		return AST_MODULE_LOAD_FAILURE;
 	}
 
 	if (!(sched = ast_sched_context_create())) {
 		ast_log(LOG_ERROR, "Could not create scheduler for contact auto-expiration\n");
-		goto error;
+		unload_module();
+		return AST_MODULE_LOAD_FAILURE;
 	}
 
 	if (ast_sched_start_thread(sched)) {
 		ast_log(LOG_ERROR, "Could not start scheduler thread for contact auto-expiration\n");
-		goto error;
+		unload_module();
+		return AST_MODULE_LOAD_FAILURE;
 	}
 
 	contact_expiration_initialize_existing();
 
 	if (ast_sorcery_observer_add(ast_sip_get_sorcery(), "contact", &contact_expiration_observer)) {
 		ast_log(LOG_ERROR, "Could not add observer for notifications about contacts for contact auto-expiration\n");
-		goto error;
+		unload_module();
+		return AST_MODULE_LOAD_FAILURE;
 	}
 
 	return AST_MODULE_LOAD_SUCCESS;
-
-error:
-	if (sched) {
-		ast_sched_context_destroy(sched);
-	}
-
-	ao2_cleanup(contact_autoexpire);
-	return AST_MODULE_LOAD_FAILURE;
-}
-
-static int unload_module(void)
-{
-	ast_sorcery_observer_remove(ast_sip_get_sorcery(), "contact", &contact_expiration_observer);
-	ast_sched_context_destroy(sched);
-	ao2_cleanup(contact_autoexpire);
-
-	return 0;
 }
 
 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Contact Auto-Expiration",
-		.support_level = AST_MODULE_SUPPORT_CORE,
-		.load = load_module,
-		.unload = unload_module,
-		.load_pri = AST_MODPRI_APP_DEPEND,
-	       );
+	.support_level = AST_MODULE_SUPPORT_CORE,
+	.load = load_module,
+	.unload = unload_module,
+	.load_pri = AST_MODPRI_APP_DEPEND,
+	);
diff --git a/res/res_pjsip_rfc3326.c b/res/res_pjsip_rfc3326.c
index 3ed8de6..ab92d6f 100644
--- a/res/res_pjsip_rfc3326.c
+++ b/res/res_pjsip_rfc3326.c
@@ -32,6 +32,7 @@
 #include "asterisk/res_pjsip_session.h"
 #include "asterisk/module.h"
 #include "asterisk/causes.h"
+#include "asterisk/threadpool.h"
 
 static void rfc3326_use_reason_header(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
 {
@@ -101,9 +102,15 @@ static void rfc3326_add_reason_header(struct ast_sip_session *session, struct pj
 
 static void rfc3326_outgoing_request(struct ast_sip_session *session, struct pjsip_tx_data *tdata)
 {
-	if ((pjsip_method_cmp(&tdata->msg->line.req.method, &pjsip_bye_method) &&
-	     pjsip_method_cmp(&tdata->msg->line.req.method, &pjsip_cancel_method)) ||
-	    !session->channel) {
+	if ((pjsip_method_cmp(&tdata->msg->line.req.method, &pjsip_bye_method)
+			&& pjsip_method_cmp(&tdata->msg->line.req.method, &pjsip_cancel_method))
+		|| !session->channel
+		/*
+		 * The session->channel has been seen to go away on us between
+		 * checks so we must also be running under the call's serializer
+		 * thread.
+		 */
+		|| session->serializer != ast_threadpool_serializer_get_current()) {
 		return;
 	}
 
@@ -114,7 +121,9 @@ static void rfc3326_outgoing_response(struct ast_sip_session *session, struct pj
 {
 	struct pjsip_status_line status = tdata->msg->line.status;
 
-	if ((status.code < 300) || !session->channel) {
+	if (status.code < 300
+		|| !session->channel
+		|| session->serializer != ast_threadpool_serializer_get_current()) {
 		return;
 	}
 
diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c
index 036a645..f733ea4 100644
--- a/res/res_pjsip_sdp_rtp.c
+++ b/res/res_pjsip_sdp_rtp.c
@@ -38,7 +38,7 @@
 #include <pjmedia.h>
 #include <pjlib.h>
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 431327 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/format.h"
@@ -50,6 +50,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 431327 $")
 #include "asterisk/sched.h"
 #include "asterisk/acl.h"
 #include "asterisk/sdp_srtp.h"
+#include "asterisk/dsp.h"
 
 #include "asterisk/res_pjsip.h"
 #include "asterisk/res_pjsip_session.h"
@@ -106,6 +107,70 @@ static void format_cap_only_type(struct ast_format_cap *caps, enum ast_media_typ
 	}
 }
 
+static int send_keepalive(const void *data)
+{
+	struct ast_sip_session_media *session_media = (struct ast_sip_session_media *) data;
+	struct ast_rtp_instance *rtp = session_media->rtp;
+	int keepalive;
+	time_t interval;
+	int send_keepalive;
+
+	if (!rtp) {
+		return 0;
+	}
+
+	keepalive = ast_rtp_instance_get_keepalive(rtp);
+
+	if (!ast_sockaddr_isnull(&session_media->direct_media_addr)) {
+		ast_debug(3, "Not sending RTP keepalive on RTP instance %p since direct media is in use\n", rtp);
+		return keepalive * 1000;
+	}
+
+	interval = time(NULL) - ast_rtp_instance_get_last_tx(rtp);
+	send_keepalive = interval >= keepalive;
+
+	ast_debug(3, "It has been %d seconds since RTP was last sent on instance %p. %sending keepalive\n",
+			(int) interval, rtp, send_keepalive ? "S" : "Not s");
+
+	if (send_keepalive) {
+		ast_rtp_instance_sendcng(rtp, 0);
+		return keepalive * 1000;
+	}
+
+	return (keepalive - interval) * 1000;
+}
+
+/*! \brief Check whether RTP is being received or not */
+static int rtp_check_timeout(const void *data)
+{
+	struct ast_sip_session_media *session_media = (struct ast_sip_session_media *)data;
+	struct ast_rtp_instance *rtp = session_media->rtp;
+	int elapsed;
+	struct ast_channel *chan;
+
+	if (!rtp) {
+		return 0;
+	}
+
+	elapsed = time(NULL) - ast_rtp_instance_get_last_rx(rtp);
+	if (elapsed < ast_rtp_instance_get_timeout(rtp)) {
+		return (ast_rtp_instance_get_timeout(rtp) - elapsed) * 1000;
+	}
+
+	chan = ast_channel_get_by_name(ast_rtp_instance_get_channel_id(rtp));
+	if (!chan) {
+		return 0;
+	}
+
+	ast_log(LOG_NOTICE, "Disconnecting channel '%s' for lack of RTP activity in %d seconds\n",
+		ast_channel_name(chan), elapsed);
+
+	ast_softhangup(chan, AST_SOFTHANGUP_DEV);
+	ast_channel_unref(chan);
+
+	return 0;
+}
+
 /*! \brief Internal function which creates an RTP instance */
 static int create_rtp(struct ast_sip_session *session, struct ast_sip_session_media *session_media, unsigned int ipv6)
 {
@@ -119,16 +184,13 @@ 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_RTCP, 1);
 	ast_rtp_instance_set_prop(session_media->rtp, AST_RTP_PROPERTY_NAT, session->endpoint->media.rtp.symmetric);
 
-	if (session->endpoint->dtmf == AST_SIP_DTMF_INBAND) {
-		ast_rtp_instance_dtmf_mode_set(session_media->rtp, AST_RTP_DTMF_MODE_INBAND);
-	}
-
 	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) {
+	if (session->endpoint->dtmf == AST_SIP_DTMF_RFC_4733 || session->endpoint->dtmf == AST_SIP_DTMF_AUTO) {
 		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) {
 		ast_rtp_instance_dtmf_mode_set(session_media->rtp, AST_RTP_DTMF_MODE_INBAND);
 	}
@@ -143,19 +205,24 @@ static int create_rtp(struct ast_sip_session *session, struct ast_sip_session_me
 				session->endpoint->media.cos_video, "SIP RTP Video");
 	}
 
+	ast_rtp_instance_set_last_rx(session_media->rtp, time(NULL));
+
 	return 0;
 }
 
-static void get_codecs(struct ast_sip_session *session, const struct pjmedia_sdp_media *stream, struct ast_rtp_codecs *codecs)
+static void get_codecs(struct ast_sip_session *session, const struct pjmedia_sdp_media *stream, struct ast_rtp_codecs *codecs,
+       struct ast_sip_session_media *session_media)
 {
 	pjmedia_sdp_attr *attr;
 	pjmedia_sdp_rtpmap *rtpmap;
 	pjmedia_sdp_fmtp fmtp;
 	struct ast_format *format;
-	int i, num = 0;
+	int i, num = 0, tel_event = 0;
 	char name[256];
 	char media[20];
 	char fmt_param[256];
+	enum ast_rtp_options options = session->endpoint->media.g726_non_standard ?
+		AST_RTP_OPT_G726_NONSTANDARD : 0;
 
 	ast_rtp_codecs_payloads_initialize(codecs);
 
@@ -174,16 +241,24 @@ static void get_codecs(struct ast_sip_session *session, const struct pjmedia_sdp
 		}
 
 		ast_copy_pj_str(name, &rtpmap->enc_name, sizeof(name));
+		if (strcmp(name, "telephone-event") == 0) {
+			tel_event++;
+		}
+
 		ast_copy_pj_str(media, (pj_str_t*)&stream->desc.media, sizeof(media));
-		ast_rtp_codecs_payloads_set_rtpmap_type_rate(codecs, NULL, pj_strtoul(&stream->desc.fmt[i]),
-							     media, name, 0, rtpmap->clock_rate);
+		ast_rtp_codecs_payloads_set_rtpmap_type_rate(codecs, NULL,
+			pj_strtoul(&stream->desc.fmt[i]), media, name, options, rtpmap->clock_rate);
 		/* Look for an optional associated fmtp attribute */
 		if (!(attr = pjmedia_sdp_media_find_attr2(stream, "fmtp", &rtpmap->pt))) {
 			continue;
 		}
 
 		if ((pjmedia_sdp_attr_get_fmtp(attr, &fmtp)) == PJ_SUCCESS) {
-			sscanf(pj_strbuf(&fmtp.fmt), "%d", &num);
+			ast_copy_pj_str(fmt_param, &fmtp.fmt, sizeof(fmt_param));
+			if (sscanf(fmt_param, "%30d", &num) != 1) {
+				continue;
+			}
+
 			if ((format = ast_rtp_codecs_get_payload_format(codecs, num))) {
 				struct ast_format *format_parsed;
 
@@ -199,7 +274,9 @@ static void get_codecs(struct ast_sip_session *session, const struct pjmedia_sdp
 			}
 		}
 	}
-
+	if (!tel_event && (session->endpoint->dtmf == AST_SIP_DTMF_AUTO)) {
+		ast_rtp_instance_dtmf_mode_set(session_media->rtp, AST_RTP_DTMF_MODE_INBAND);
+	}
 	/* Get the packetization, if it exists */
 	if ((attr = pjmedia_sdp_media_find_attr2(stream, "ptime", NULL))) {
 		unsigned long framing = pj_strtoul(pj_strltrim(&attr->value));
@@ -220,6 +297,7 @@ static int set_caps(struct ast_sip_session *session, struct ast_sip_session_medi
 	int fmts = 0;
 	int direct_media_enabled = !ast_sockaddr_isnull(&session_media->direct_media_addr) &&
 		ast_format_cap_count(session->direct_media_cap);
+	int dsp_features = 0;
 
 	if (!(caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT)) ||
 	    !(peer = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT)) ||
@@ -237,14 +315,14 @@ static int set_caps(struct ast_sip_session *session, struct ast_sip_session_medi
 	}
 
 	/* get the capabilities on the peer */
-	get_codecs(session, stream, &codecs);
+	get_codecs(session, stream, &codecs,  session_media);
 	ast_rtp_codecs_payload_formats(&codecs, peer, &fmts);
 
 	/* get the joint capabilities between peer and endpoint */
 	ast_format_cap_get_compatible(caps, peer, joint);
 	if (!ast_format_cap_count(joint)) {
-		struct ast_str *usbuf = ast_str_alloca(64);
-		struct ast_str *thembuf = ast_str_alloca(64);
+		struct ast_str *usbuf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+		struct ast_str *thembuf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 		ast_rtp_codecs_payloads_destroy(&codecs);
 		ast_log(LOG_NOTICE, "No joint capabilities for '%s' media stream between our configuration(%s) and incoming SDP(%s)\n",
@@ -255,61 +333,65 @@ static int set_caps(struct ast_sip_session *session, struct ast_sip_session_medi
 	}
 
 	ast_rtp_codecs_payloads_copy(&codecs, ast_rtp_instance_get_codecs(session_media->rtp),
-				     session_media->rtp);
+		session_media->rtp);
 
 	ast_format_cap_append_from_cap(session->req_caps, joint, AST_MEDIA_TYPE_UNKNOWN);
 
 	if (session->channel) {
-		struct ast_format *fmt;
-
 		ast_channel_lock(session->channel);
 		ast_format_cap_remove_by_type(caps, AST_MEDIA_TYPE_UNKNOWN);
-		ast_format_cap_append_from_cap(caps, ast_channel_nativeformats(session->channel), AST_MEDIA_TYPE_UNKNOWN);
+		ast_format_cap_append_from_cap(caps, ast_channel_nativeformats(session->channel),
+			AST_MEDIA_TYPE_UNKNOWN);
 		ast_format_cap_remove_by_type(caps, media_type);
-
-		/*
-		 * XXX Historically we picked the "best" joint format to use
-		 * and stuck with it.  It would be nice to just append the
-		 * determined joint media capabilities to give translation
-		 * more formats to choose from when necessary.  Unfortunately,
-		 * there are some areas of the system where this doesn't work
-		 * very well. (The softmix bridge in particular is reluctant
-		 * to pick higher fidelity formats and has a problem with
-		 * asymmetric sample rates.)
-		 */
-		fmt = ast_format_cap_get_format(joint, 0);
-		ast_format_cap_append(caps, fmt, 0);
+		ast_format_cap_append_from_cap(caps, joint, media_type);
 
 		/*
 		 * Apply the new formats to the channel, potentially changing
 		 * raw read/write formats and translation path while doing so.
 		 */
 		ast_channel_nativeformats_set(session->channel, caps);
-		ast_set_read_format(session->channel, ast_channel_readformat(session->channel));
-		ast_set_write_format(session->channel, ast_channel_writeformat(session->channel));
+		if (media_type == AST_MEDIA_TYPE_AUDIO) {
+			ast_set_read_format(session->channel, ast_channel_readformat(session->channel));
+			ast_set_write_format(session->channel, ast_channel_writeformat(session->channel));
+		}
+		if ((session->endpoint->dtmf == AST_SIP_DTMF_AUTO)
+		    && (ast_rtp_instance_dtmf_mode_get(session_media->rtp) == AST_RTP_DTMF_MODE_RFC2833)
+		    && (session->dsp)) {
+			dsp_features = ast_dsp_get_features(session->dsp);
+			dsp_features &= ~DSP_FEATURE_DIGIT_DETECT;
+			if (dsp_features) {
+				ast_dsp_set_features(session->dsp, dsp_features);
+			} else {
+				ast_dsp_free(session->dsp);
+				session->dsp = NULL;
+			}
+		}
 		ast_channel_unlock(session->channel);
-
-		ao2_ref(fmt, -1);
 	}
 
 	ast_rtp_codecs_payloads_destroy(&codecs);
 	return 0;
 }
 
-static pjmedia_sdp_attr* generate_rtpmap_attr(pjmedia_sdp_media *media, pj_pool_t *pool, int rtp_code,
-					      int asterisk_format, struct ast_format *format, int code)
+static pjmedia_sdp_attr* generate_rtpmap_attr(struct ast_sip_session *session, pjmedia_sdp_media *media, pj_pool_t *pool,
+					      int rtp_code, int asterisk_format, struct ast_format *format, int code)
 {
 	pjmedia_sdp_rtpmap rtpmap;
 	pjmedia_sdp_attr *attr = NULL;
 	char tmp[64];
+	enum ast_rtp_options options = session->endpoint->media.g726_non_standard ?
+		AST_RTP_OPT_G726_NONSTANDARD : 0;
 
 	snprintf(tmp, sizeof(tmp), "%d", rtp_code);
 	pj_strdup2(pool, &media->desc.fmt[media->desc.fmt_count++], tmp);
 	rtpmap.pt = media->desc.fmt[media->desc.fmt_count - 1];
 	rtpmap.clock_rate = ast_rtp_lookup_sample_rate2(asterisk_format, format, code);
-	pj_strdup2(pool, &rtpmap.enc_name, ast_rtp_lookup_mime_subtype2(asterisk_format, format, code, 0));
-	rtpmap.param.slen = 0;
-	rtpmap.param.ptr = NULL;
+	pj_strdup2(pool, &rtpmap.enc_name, ast_rtp_lookup_mime_subtype2(asterisk_format, format, code, options));
+	if (!pj_stricmp2(&rtpmap.enc_name, "opus")) {
+		pj_cstr(&rtpmap.param, "2");
+	} else {
+		pj_cstr(&rtpmap.param, NULL);
+	}
 
 	pjmedia_sdp_rtpmap_to_attr(pool, &rtpmap, &attr);
 
@@ -399,6 +481,7 @@ static void add_ice_to_stream(struct ast_sip_session *session, struct ast_sip_se
 	}
 
 	ao2_iterator_destroy(&it_candidates);
+	ao2_ref(candidates, -1);
 }
 
 /*! \brief Function which processes ICE attributes in an audio stream */
@@ -522,6 +605,9 @@ static enum ast_sip_session_media_encryption get_media_encryption_type(pj_str_t
 
 	*optimistic = 0;
 
+	if (!transport_str) {
+		return AST_SIP_MEDIA_TRANSPORT_INVALID;
+	}
 	if (strstr(transport_str, "UDP/TLS")) {
 		return AST_SIP_MEDIA_ENCRYPT_DTLS;
 	} else if (strstr(transport_str, "SAVP")) {
@@ -760,7 +846,7 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct
 					 const struct pjmedia_sdp_session *sdp, const struct pjmedia_sdp_media *stream)
 {
 	char host[NI_MAXHOST];
-	RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free_ptr);
+	RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free);
 	enum ast_media_type media_type = stream_to_media_type(session_media->stream_type);
 	enum ast_sip_session_media_encryption encryption = AST_SIP_MEDIA_ENCRYPT_NONE;
 	int res;
@@ -943,13 +1029,13 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
 	static const pj_str_t STR_IP6 = { "IP6", 3};
 	static const pj_str_t STR_SENDRECV = { "sendrecv", 8 };
 	pjmedia_sdp_media *media;
-	char hostip[PJ_INET6_ADDRSTRLEN+2];
+	const char *hostip = NULL;
 	struct ast_sockaddr addr;
 	char tmp[512];
 	pj_str_t stmp;
 	pjmedia_sdp_attr *attr;
 	int index = 0;
-	int noncodec = (session->endpoint->dtmf == AST_SIP_DTMF_RFC_4733) ? AST_RTP_DTMF : 0;
+	int noncodec = (session->endpoint->dtmf == AST_SIP_DTMF_RFC_4733 || session->endpoint->dtmf == AST_SIP_DTMF_AUTO) ? 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);
@@ -991,16 +1077,16 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
 
 	/* Add connection level details */
 	if (direct_media_enabled) {
-		ast_copy_string(hostip, ast_sockaddr_stringify_fmt(&session_media->direct_media_addr, AST_SOCKADDR_STR_ADDR), sizeof(hostip));
+		hostip = ast_sockaddr_stringify_fmt(&session_media->direct_media_addr, AST_SOCKADDR_STR_ADDR);
 	} else if (ast_strlen_zero(session->endpoint->media.address)) {
-		pj_sockaddr localaddr;
-
-		if (pj_gethostip(session->endpoint->media.rtp.ipv6 ? pj_AF_INET6() : pj_AF_INET(), &localaddr)) {
-			return -1;
-		}
-		pj_sockaddr_print(&localaddr, hostip, sizeof(hostip), 2);
+		hostip = ast_sip_get_host_ip_string(session->endpoint->media.rtp.ipv6 ? pj_AF_INET6() : pj_AF_INET());
 	} else {
-		ast_copy_string(hostip, session->endpoint->media.address, sizeof(hostip));
+		hostip = session->endpoint->media.address;
+	}
+
+	if (ast_strlen_zero(hostip)) {
+		ast_log(LOG_ERROR, "No local host IP available for stream %s\n", session_media->stream_type);
+		return -1;
 	}
 
 	media->conn->net_type = STR_IN;
@@ -1041,7 +1127,7 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
 			continue;
 		}
 
-		if (!(attr = generate_rtpmap_attr(media, pool, rtp_code, 1, format, 0))) {
+		if (!(attr = generate_rtpmap_attr(session, media, pool, rtp_code, 1, format, 0))) {
 			ao2_ref(format, -1);
 			continue;
 		}
@@ -1061,12 +1147,16 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
 	/* Add non-codec formats */
 	if (media_type != AST_MEDIA_TYPE_VIDEO) {
 		for (index = 1LL; index <= AST_RTP_MAX; index <<= 1) {
-			if (!(noncodec & index) || (rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(session_media->rtp),
-											   0, NULL, index)) == -1) {
+			if (!(noncodec & index)) {
+				continue;
+			}
+			rtp_code = ast_rtp_codecs_payload_code(
+				ast_rtp_instance_get_codecs(session_media->rtp), 0, NULL, index);
+			if (rtp_code == -1) {
 				continue;
 			}
 
-			if (!(attr = generate_rtpmap_attr(media, pool, rtp_code, 0, NULL, index))) {
+			if (!(attr = generate_rtpmap_attr(session, media, pool, rtp_code, 0, NULL, index))) {
 				continue;
 			}
 
@@ -1117,7 +1207,7 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, struct a
 				       const struct pjmedia_sdp_session *local, const struct pjmedia_sdp_media *local_stream,
 				       const struct pjmedia_sdp_session *remote, const struct pjmedia_sdp_media *remote_stream)
 {
-	RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free_ptr);
+	RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free);
 	enum ast_media_type media_type = stream_to_media_type(session_media->stream_type);
 	char host[NI_MAXHOST];
 	int fdno, res;
@@ -1164,7 +1254,7 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, struct a
 	/* Apply connection information to the RTP instance */
 	ast_sockaddr_set_port(addrs, remote_stream->desc.port);
 	ast_rtp_instance_set_remote_address(session_media->rtp, addrs);
-	if (set_caps(session, session_media, local_stream)) {
+	if (set_caps(session, session_media, remote_stream)) {
 		return 1;
 	}
 
@@ -1182,12 +1272,17 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, struct a
 
 	/* audio stream handles music on hold */
 	if (media_type != AST_MEDIA_TYPE_AUDIO) {
+		if ((pjmedia_sdp_neg_was_answer_remote(session->inv_session->neg) == PJ_FALSE)
+			&& (session->inv_session->state == PJSIP_INV_STATE_CONFIRMED)) {
+			ast_queue_control(session->channel, AST_CONTROL_UPDATE_RTP_PEER);
+		}
 		return 1;
 	}
 
 	if (ast_sockaddr_isnull(addrs) ||
 		ast_sockaddr_is_any(addrs) ||
-		pjmedia_sdp_media_find_attr2(remote_stream, "sendonly", NULL)) {
+		pjmedia_sdp_media_find_attr2(remote_stream, "sendonly", NULL) ||
+		pjmedia_sdp_media_find_attr2(remote_stream, "inactive", NULL)) {
 		if (!session_media->held) {
 			/* The remote side has put us on hold */
 			ast_queue_hold(session->channel, session->endpoint->mohsuggest);
@@ -1200,11 +1295,48 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, struct a
 		ast_queue_unhold(session->channel);
 		ast_queue_frame(session->channel, &ast_null_frame);
 		session_media->held = 0;
+	} else if ((pjmedia_sdp_neg_was_answer_remote(session->inv_session->neg) == PJ_FALSE)
+		&& (session->inv_session->state == PJSIP_INV_STATE_CONFIRMED)) {
+		ast_queue_control(session->channel, AST_CONTROL_UPDATE_RTP_PEER);
 	}
 
 	/* This purposely resets the encryption to the configured in case it gets added later */
 	session_media->encryption = session->endpoint->media.rtp.encryption;
 
+	if (session->endpoint->media.rtp.keepalive > 0 &&
+			stream_to_media_type(session_media->stream_type) == AST_MEDIA_TYPE_AUDIO) {
+		ast_rtp_instance_set_keepalive(session_media->rtp, session->endpoint->media.rtp.keepalive);
+		/* Schedule the initial keepalive early in case this is being used to punch holes through
+		 * a NAT. This way there won't be an awkward delay before media starts flowing in some
+		 * scenarios.
+		 */
+		AST_SCHED_DEL(sched, session_media->keepalive_sched_id);
+		session_media->keepalive_sched_id = ast_sched_add_variable(sched, 500, send_keepalive,
+			session_media, 1);
+	}
+
+	/* As the channel lock is not held during this process the scheduled item won't block if
+	 * it is hanging up the channel at the same point we are applying this negotiated SDP.
+	 */
+	AST_SCHED_DEL(sched, session_media->timeout_sched_id);
+
+	/* Due to the fact that we only ever have one scheduled timeout item for when we are both
+	 * off hold and on hold we don't need to store the two timeouts differently on the RTP
+	 * instance itself.
+	 */
+	ast_rtp_instance_set_timeout(session_media->rtp, 0);
+	if (session->endpoint->media.rtp.timeout && !session_media->held) {
+		ast_rtp_instance_set_timeout(session_media->rtp, session->endpoint->media.rtp.timeout);
+	} else if (session->endpoint->media.rtp.timeout_hold && session_media->held) {
+		ast_rtp_instance_set_timeout(session_media->rtp, session->endpoint->media.rtp.timeout_hold);
+	}
+
+	if (ast_rtp_instance_get_timeout(session_media->rtp)) {
+		session_media->timeout_sched_id = ast_sched_add_variable(sched,
+			ast_rtp_instance_get_timeout(session_media->rtp) * 1000, rtp_check_timeout,
+			session_media, 1);
+	}
+
 	return 1;
 }
 
@@ -1230,11 +1362,23 @@ static void change_outgoing_sdp_stream_media_address(pjsip_tx_data *tdata, struc
 	pj_strdup2(tdata->pool, &stream->conn->addr, transport->external_media_address);
 }
 
+/*! \brief Function which stops the RTP instance */
+static void stream_stop(struct ast_sip_session_media *session_media)
+{
+	if (!session_media->rtp) {
+		return;
+	}
+
+	AST_SCHED_DEL(sched, session_media->keepalive_sched_id);
+	AST_SCHED_DEL(sched, session_media->timeout_sched_id);
+	ast_rtp_instance_stop(session_media->rtp);
+}
+
 /*! \brief Function which destroys the RTP instance when session ends */
 static void stream_destroy(struct ast_sip_session_media *session_media)
 {
 	if (session_media->rtp) {
-		ast_rtp_instance_stop(session_media->rtp);
+		stream_stop(session_media);
 		ast_rtp_instance_destroy(session_media->rtp);
 	}
 	session_media->rtp = NULL;
@@ -1247,6 +1391,7 @@ static struct ast_sip_session_sdp_handler audio_sdp_handler = {
 	.create_outgoing_sdp_stream = create_outgoing_sdp_stream,
 	.apply_negotiated_sdp_stream = apply_negotiated_sdp_stream,
 	.change_outgoing_sdp_stream_media_address = change_outgoing_sdp_stream_media_address,
+	.stream_stop = stream_stop,
 	.stream_destroy = stream_destroy,
 };
 
@@ -1257,20 +1402,24 @@ static struct ast_sip_session_sdp_handler video_sdp_handler = {
 	.create_outgoing_sdp_stream = create_outgoing_sdp_stream,
 	.apply_negotiated_sdp_stream = apply_negotiated_sdp_stream,
 	.change_outgoing_sdp_stream_media_address = change_outgoing_sdp_stream_media_address,
+	.stream_stop = stream_stop,
 	.stream_destroy = stream_destroy,
 };
 
 static int video_info_incoming_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
 {
-	struct pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
+	struct pjsip_transaction *tsx;
 	pjsip_tx_data *tdata;
 
-	if (!ast_sip_is_content_type(&rdata->msg_info.msg->body->content_type,
-				     "application",
-				     "media_control+xml")) {
+	if (!session->channel
+		|| !ast_sip_is_content_type(&rdata->msg_info.msg->body->content_type,
+			"application",
+			"media_control+xml")) {
 		return 0;
 	}
 
+	tsx = pjsip_rdata_get_tsx(rdata);
+
 	ast_queue_control(session->channel, AST_CONTROL_VIDUPDATE);
 
 	if (pjsip_dlg_create_response(session->inv_session->dlg, rdata, 200, NULL, &tdata) == PJ_SUCCESS) {
diff --git a/res/res_pjsip_send_to_voicemail.c b/res/res_pjsip_send_to_voicemail.c
index 97f55d3..3a57aea 100644
--- a/res/res_pjsip_send_to_voicemail.c
+++ b/res/res_pjsip_send_to_voicemail.c
@@ -119,13 +119,17 @@ static int has_call_feature(pjsip_rx_data *rdata)
 
 static int handle_incoming_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
 {
-
 	struct ast_datastore *sip_session_datastore;
 	struct ast_channel *other_party;
+	int has_feature;
+	int has_reason;
 
-	int has_feature = has_call_feature(rdata);
-	int has_reason = has_diversion_reason(rdata);
+	if (!session->channel) {
+		return 0;
+	}
 
+	has_feature = has_call_feature(rdata);
+	has_reason = has_diversion_reason(rdata);
 	if (!has_feature && !has_reason) {
 		/* If we don't have a call feature or diversion reason or if
 		   it's not a feature this module is related to then there
diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c
index 4467203..6044ceb 100644
--- a/res/res_pjsip_session.c
+++ b/res/res_pjsip_session.c
@@ -362,6 +362,13 @@ static int handle_negotiated_sdp_session_media(void *obj, void *arg, int flags)
 			}
 		}
 	}
+
+	if (session_media->handler && session_media->handler->stream_stop) {
+		ast_debug(1, "Stopping SDP media stream '%s' as it is not currently negotiated\n",
+			session_media->stream_type);
+		session_media->handler->stream_stop(session_media);
+	}
+
 	return CMP_MATCH;
 }
 
@@ -505,6 +512,40 @@ void ast_sip_session_remove_datastore(struct ast_sip_session *session, const cha
 	ao2_callback(session->datastores, OBJ_KEY | OBJ_UNLINK | OBJ_NODATA, NULL, (void *) name);
 }
 
+enum delayed_method {
+	DELAYED_METHOD_INVITE,
+	DELAYED_METHOD_UPDATE,
+	DELAYED_METHOD_BYE,
+};
+
+/*!
+ * \internal
+ * \brief Convert delayed method enum value to to a string.
+ * \since 13.3.0
+ *
+ * \param method Delayed method enum value to convert to a string.
+ *
+ * \return String value of delayed method.
+ */
+static const char *delayed_method2str(enum delayed_method method)
+{
+	const char *str = "<unknown>";
+
+	switch (method) {
+	case DELAYED_METHOD_INVITE:
+		str = "INVITE";
+		break;
+	case DELAYED_METHOD_UPDATE:
+		str = "UPDATE";
+		break;
+	case DELAYED_METHOD_BYE:
+		str = "BYE";
+		break;
+	}
+
+	return str;
+}
+
 /*!
  * \brief Structure used for sending delayed requests
  *
@@ -514,7 +555,7 @@ void ast_sip_session_remove_datastore(struct ast_sip_session *session, const cha
  */
 struct ast_sip_session_delayed_request {
 	/*! Method of the request */
-	char method[15];
+	enum delayed_method method;
 	/*! Callback to call when the delayed request is created. */
 	ast_sip_session_request_creation_cb on_request_creation;
 	/*! Callback to call when the delayed request SDP is created */
@@ -526,17 +567,19 @@ struct ast_sip_session_delayed_request {
 	AST_LIST_ENTRY(ast_sip_session_delayed_request) next;
 };
 
-static struct ast_sip_session_delayed_request *delayed_request_alloc(const char *method,
-		ast_sip_session_request_creation_cb on_request_creation,
-		ast_sip_session_sdp_creation_cb on_sdp_creation,
-		ast_sip_session_response_cb on_response,
-		int generate_new_sdp)
+static struct ast_sip_session_delayed_request *delayed_request_alloc(
+	enum delayed_method method,
+	ast_sip_session_request_creation_cb on_request_creation,
+	ast_sip_session_sdp_creation_cb on_sdp_creation,
+	ast_sip_session_response_cb on_response,
+	int generate_new_sdp)
 {
 	struct ast_sip_session_delayed_request *delay = ast_calloc(1, sizeof(*delay));
+
 	if (!delay) {
 		return NULL;
 	}
-	ast_copy_string(delay->method, method, sizeof(delay->method));
+	delay->method = method;
 	delay->on_request_creation = on_request_creation;
 	delay->on_sdp_creation = on_sdp_creation;
 	delay->on_response = on_response;
@@ -546,51 +589,187 @@ static struct ast_sip_session_delayed_request *delayed_request_alloc(const char
 
 static int send_delayed_request(struct ast_sip_session *session, struct ast_sip_session_delayed_request *delay)
 {
-	ast_debug(3, "Sending delayed %s request to %s\n", delay->method, ast_sorcery_object_get_id(session->endpoint));
+	ast_debug(3, "Endpoint '%s(%s)' sending delayed %s request.\n",
+		ast_sorcery_object_get_id(session->endpoint),
+		session->channel ? ast_channel_name(session->channel) : "",
+		delayed_method2str(delay->method));
 
-	if (!strcmp(delay->method, "INVITE")) {
+	switch (delay->method) {
+	case DELAYED_METHOD_INVITE:
 		ast_sip_session_refresh(session, delay->on_request_creation,
-				delay->on_sdp_creation, delay->on_response, AST_SIP_SESSION_REFRESH_METHOD_INVITE, delay->generate_new_sdp);
-	} else if (!strcmp(delay->method, "UPDATE")) {
+			delay->on_sdp_creation, delay->on_response,
+			AST_SIP_SESSION_REFRESH_METHOD_INVITE, delay->generate_new_sdp);
+		return 0;
+	case DELAYED_METHOD_UPDATE:
 		ast_sip_session_refresh(session, delay->on_request_creation,
-				delay->on_sdp_creation, delay->on_response, AST_SIP_SESSION_REFRESH_METHOD_UPDATE, delay->generate_new_sdp);
-	} else {
-		ast_log(LOG_WARNING, "Unexpected delayed %s request with no existing request structure\n", delay->method);
-		return -1;
+			delay->on_sdp_creation, delay->on_response,
+			AST_SIP_SESSION_REFRESH_METHOD_UPDATE, delay->generate_new_sdp);
+		return 0;
+	case DELAYED_METHOD_BYE:
+		ast_sip_session_terminate(session, 0);
+		return 0;
 	}
-	return 0;
+	ast_log(LOG_WARNING, "Don't know how to send delayed %s(%d) request.\n",
+		delayed_method2str(delay->method), delay->method);
+	return -1;
 }
 
-static int queued_delayed_request_send(void *data)
+/*!
+ * \internal
+ * \brief The current INVITE transaction is in the PROCEEDING state.
+ * \since 13.3.0
+ *
+ * \param vsession Session object.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int invite_proceeding(void *vsession)
 {
-	RAII_VAR(struct ast_sip_session *, session, data, ao2_cleanup);
-	RAII_VAR(struct ast_sip_session_delayed_request *, delay, NULL, ast_free_ptr);
+	struct ast_sip_session *session = vsession;
+	struct ast_sip_session_delayed_request *delay;
+	int found = 0;
+	int res = 0;
 
-	delay = AST_LIST_REMOVE_HEAD(&session->delayed_requests, next);
-	if (!delay) {
-		return 0;
+	AST_LIST_TRAVERSE_SAFE_BEGIN(&session->delayed_requests, delay, next) {
+		switch (delay->method) {
+		case DELAYED_METHOD_INVITE:
+			break;
+		case DELAYED_METHOD_UPDATE:
+			AST_LIST_REMOVE_CURRENT(next);
+			res = send_delayed_request(session, delay);
+			ast_free(delay);
+			found = 1;
+			break;
+		case DELAYED_METHOD_BYE:
+			/* A BYE is pending so don't bother anymore. */
+			found = 1;
+			break;
+		}
+		if (found) {
+			break;
+		}
 	}
+	AST_LIST_TRAVERSE_SAFE_END;
 
-	return send_delayed_request(session, delay);
+	ao2_ref(session, -1);
+	return res;
 }
 
-static void queue_delayed_request(struct ast_sip_session *session)
+/*!
+ * \internal
+ * \brief The current INVITE transaction is in the TERMINATED state.
+ * \since 13.3.0
+ *
+ * \param vsession Session object.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int invite_terminated(void *vsession)
 {
-	if (AST_LIST_EMPTY(&session->delayed_requests)) {
-		/* No delayed request to send, so just return */
-		return;
+	struct ast_sip_session *session = vsession;
+	struct ast_sip_session_delayed_request *delay;
+	int found = 0;
+	int res = 0;
+	int timer_running;
+
+	/* re-INVITE collision timer running? */
+	timer_running = pj_timer_entry_running(&session->rescheduled_reinvite);
+
+	AST_LIST_TRAVERSE_SAFE_BEGIN(&session->delayed_requests, delay, next) {
+		switch (delay->method) {
+		case DELAYED_METHOD_INVITE:
+			if (!timer_running) {
+				found = 1;
+			}
+			break;
+		case DELAYED_METHOD_UPDATE:
+		case DELAYED_METHOD_BYE:
+			found = 1;
+			break;
+		}
+		if (found) {
+			AST_LIST_REMOVE_CURRENT(next);
+			res = send_delayed_request(session, delay);
+			ast_free(delay);
+			break;
+		}
 	}
+	AST_LIST_TRAVERSE_SAFE_END;
 
-	ast_debug(3, "Queuing delayed request to run for %s\n",
-			ast_sorcery_object_get_id(session->endpoint));
+	ao2_ref(session, -1);
+	return res;
+}
+
+/*!
+ * \internal
+ * \brief INVITE collision timeout.
+ * \since 13.3.0
+ *
+ * \param vsession Session object.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int invite_collision_timeout(void *vsession)
+{
+	struct ast_sip_session *session = vsession;
+	int res;
 
+	if (session->inv_session->invite_tsx) {
+		/*
+		 * INVITE transaction still active.  Let it send
+		 * the collision re-INVITE when it terminates.
+		 */
+		ao2_ref(session, -1);
+		res = 0;
+	} else {
+		res = invite_terminated(session);
+	}
+
+	return res;
+}
+
+/*!
+ * \internal
+ * \brief The current UPDATE transaction is in the COMPLETED state.
+ * \since 13.3.0
+ *
+ * \param vsession Session object.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int update_completed(void *vsession)
+{
+	struct ast_sip_session *session = vsession;
+	int res;
+
+	if (session->inv_session->invite_tsx) {
+		res = invite_proceeding(session);
+	} else {
+		res = invite_terminated(session);
+	}
+
+	return res;
+}
+
+static void check_delayed_requests(struct ast_sip_session *session,
+	int (*cb)(void *vsession))
+{
 	ao2_ref(session, +1);
-	ast_sip_push_task(session->serializer, queued_delayed_request_send, session);
+	if (ast_sip_push_task(session->serializer, cb, session)) {
+		ao2_ref(session, -1);
+	}
 }
 
-static int delay_request(struct ast_sip_session *session, ast_sip_session_request_creation_cb on_request,
-		ast_sip_session_sdp_creation_cb on_sdp_creation, ast_sip_session_response_cb on_response,
-		int generate_new_sdp, const char *method)
+static int delay_request(struct ast_sip_session *session,
+	ast_sip_session_request_creation_cb on_request,
+	ast_sip_session_sdp_creation_cb on_sdp_creation,
+	ast_sip_session_response_cb on_response,
+	int generate_new_sdp,
+	enum delayed_method method)
 {
 	struct ast_sip_session_delayed_request *delay = delayed_request_alloc(method,
 			on_request, on_sdp_creation, on_response, generate_new_sdp);
@@ -599,7 +778,12 @@ static int delay_request(struct ast_sip_session *session, ast_sip_session_reques
 		return -1;
 	}
 
-	AST_LIST_INSERT_TAIL(&session->delayed_requests, delay, next);
+	if (method == DELAYED_METHOD_BYE) {
+		/* Send BYE as early as possible */
+		AST_LIST_INSERT_HEAD(&session->delayed_requests, delay, next);
+	} else {
+		AST_LIST_INSERT_TAIL(&session->delayed_requests, delay, next);
+	}
 	return 0;
 }
 
@@ -635,19 +819,21 @@ int ast_sip_session_refresh(struct ast_sip_session *session,
 
 	/* If the dialog has not yet been established we have to defer until it has */
 	if (inv_session->dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED) {
-		ast_debug(3, "Delaying sending request to %s because dialog has not been established...\n",
+		ast_debug(3, "Delay sending request to %s because dialog has not been established...\n",
 			ast_sorcery_object_get_id(session->endpoint));
-		return delay_request(session, on_request_creation, on_sdp_creation, on_response, generate_new_sdp,
-			method == AST_SIP_SESSION_REFRESH_METHOD_INVITE ? "INVITE" : "UPDATE");
+		return delay_request(session, on_request_creation, on_sdp_creation, on_response,
+			generate_new_sdp,
+			method == AST_SIP_SESSION_REFRESH_METHOD_INVITE
+				? DELAYED_METHOD_INVITE : DELAYED_METHOD_UPDATE);
 	}
 
 	if (method == AST_SIP_SESSION_REFRESH_METHOD_INVITE) {
 		if (inv_session->invite_tsx) {
 			/* We can't send a reinvite yet, so delay it */
-			ast_debug(3, "Delaying sending reinvite to %s because of outstanding transaction...\n",
+			ast_debug(3, "Delay sending reinvite to %s because of outstanding transaction...\n",
 					ast_sorcery_object_get_id(session->endpoint));
-			return delay_request(session, on_request_creation, on_sdp_creation, on_response,
-				generate_new_sdp, "INVITE");
+			return delay_request(session, on_request_creation, on_sdp_creation,
+				on_response, generate_new_sdp, DELAYED_METHOD_INVITE);
 		} else if (inv_session->state != PJSIP_INV_STATE_CONFIRMED) {
 			/* Initial INVITE transaction failed to progress us to a confirmed state
 			 * which means re-invites are not possible
@@ -661,10 +847,12 @@ int ast_sip_session_refresh(struct ast_sip_session *session,
 	if (generate_new_sdp) {
 		/* SDP can only be generated if current negotiation has already completed */
 		if (pjmedia_sdp_neg_get_state(inv_session->neg) != PJMEDIA_SDP_NEG_STATE_DONE) {
-			ast_debug(3, "Delaying session refresh with new SDP to %s because SDP negotiation is not yet done...\n",
+			ast_debug(3, "Delay session refresh with new SDP to %s because SDP negotiation is not yet done...\n",
 				ast_sorcery_object_get_id(session->endpoint));
-			return delay_request(session, on_request_creation, on_sdp_creation, on_response, generate_new_sdp,
-				method == AST_SIP_SESSION_REFRESH_METHOD_INVITE ? "INVITE" : "UPDATE");
+			return delay_request(session, on_request_creation, on_sdp_creation,
+				on_response, generate_new_sdp,
+				method == AST_SIP_SESSION_REFRESH_METHOD_INVITE
+					? DELAYED_METHOD_INVITE : DELAYED_METHOD_UPDATE);
 		}
 
 		new_sdp = generate_session_refresh_sdp(session);
@@ -806,7 +994,8 @@ static pj_bool_t session_reinvite_on_rx_request(pjsip_rx_data *rdata)
 
 	if (rdata->msg_info.msg->line.req.method.id != PJSIP_INVITE_METHOD ||
 		!(dlg = pjsip_ua_find_dialog(&rdata->msg_info.cid->id, &rdata->msg_info.to->tag, &rdata->msg_info.from->tag, PJ_FALSE)) ||
-		!(session = ast_sip_dialog_get_session(dlg))) {
+		!(session = ast_sip_dialog_get_session(dlg)) ||
+		!session->channel) {
 		return PJ_FALSE;
 	}
 
@@ -857,7 +1046,10 @@ void ast_sip_session_resume_reinvite(struct ast_sip_session *session)
 		return;
 	}
 
-	pjsip_endpt_process_rx_data(ast_sip_get_pjsip_endpoint(), session->deferred_reinvite, NULL, NULL);
+	if (session->channel) {
+		pjsip_endpt_process_rx_data(ast_sip_get_pjsip_endpoint(),
+			session->deferred_reinvite, NULL, NULL);
+	}
 	pjsip_rx_data_free_cloned(session->deferred_reinvite);
 	session->deferred_reinvite = NULL;
 }
@@ -976,7 +1168,7 @@ static void session_destructor(void *obj)
 	struct ast_sip_session_delayed_request *delay;
 
 	ast_debug(3, "Destroying SIP session with endpoint %s\n",
-			ast_sorcery_object_get_id(session->endpoint));
+		session->endpoint ? ast_sorcery_object_get_id(session->endpoint) : "<none>");
 
 	while ((supplement = AST_LIST_REMOVE_HEAD(&session->supplements, next))) {
 		if (supplement->session_destroy) {
@@ -995,6 +1187,7 @@ static void session_destructor(void *obj)
 	}
 	ast_party_id_free(&session->id);
 	ao2_cleanup(session->endpoint);
+	ao2_cleanup(session->aor);
 	ao2_cleanup(session->contact);
 	ao2_cleanup(session->req_caps);
 	ao2_cleanup(session->direct_media_cap);
@@ -1026,13 +1219,16 @@ static int add_supplements(struct ast_sip_session *session)
 static int add_session_media(void *obj, void *arg, int flags)
 {
 	struct sdp_handler_list *handler_list = obj;
-	struct ast_sip_session * session = arg;
+	struct ast_sip_session *session = arg;
 	RAII_VAR(struct ast_sip_session_media *, session_media, NULL, ao2_cleanup);
+
 	session_media = ao2_alloc(sizeof(*session_media) + strlen(handler_list->stream_type), session_media_dtor);
 	if (!session_media) {
 		return CMP_STOP;
 	}
 	session_media->encryption = session->endpoint->media.rtp.encryption;
+	session_media->keepalive_sched_id = -1;
+	session_media->timeout_sched_id = -1;
 	/* Safe use of strcpy */
 	strcpy(session_media->stream_type, handler_list->stream_type);
 	ao2_link(session->media, session_media);
@@ -1067,9 +1263,11 @@ struct ast_sip_channel_pvt *ast_sip_channel_pvt_alloc(void *pvt, struct ast_sip_
 struct ast_sip_session *ast_sip_session_alloc(struct ast_sip_endpoint *endpoint,
 	struct ast_sip_contact *contact, pjsip_inv_session *inv_session)
 {
-	RAII_VAR(struct ast_sip_session *, session, ao2_alloc(sizeof(*session), session_destructor), ao2_cleanup);
+	RAII_VAR(struct ast_sip_session *, session, NULL, ao2_cleanup);
 	struct ast_sip_session_supplement *iter;
 	int dsp_features = 0;
+
+	session = ao2_alloc(sizeof(*session), session_destructor);
 	if (!session) {
 		return NULL;
 	}
@@ -1099,8 +1297,13 @@ struct ast_sip_session *ast_sip_session_alloc(struct ast_sip_endpoint *endpoint,
 	session->contact = ao2_bump(contact);
 	session->inv_session = inv_session;
 	session->req_caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
+	if (!session->req_caps) {
+		/* Release the ref held by session->inv_session */
+		ao2_ref(session, -1);
+		return NULL;
+	}
 
-	if (endpoint->dtmf == AST_SIP_DTMF_INBAND) {
+	if ((endpoint->dtmf == AST_SIP_DTMF_INBAND) || (endpoint->dtmf == AST_SIP_DTMF_AUTO)) {
 		dsp_features |= DSP_FEATURE_DIGIT_DETECT;
 	}
 
@@ -1110,6 +1313,7 @@ struct ast_sip_session *ast_sip_session_alloc(struct ast_sip_endpoint *endpoint,
 
 	if (dsp_features) {
 		if (!(session->dsp = ast_dsp_new())) {
+			/* Release the ref held by session->inv_session */
 			ao2_ref(session, -1);
 			return NULL;
 		}
@@ -1118,6 +1322,7 @@ struct ast_sip_session *ast_sip_session_alloc(struct ast_sip_endpoint *endpoint,
 	}
 
 	if (add_supplements(session)) {
+		/* Release the ref held by session->inv_session */
 		ao2_ref(session, -1);
 		return NULL;
 	}
@@ -1235,16 +1440,95 @@ void ast_sip_session_unsuspend(struct ast_sip_session *session)
 	ao2_ref(suspender, -1);
 }
 
-static int session_outbound_auth(pjsip_dialog *dlg, pjsip_tx_data *tdata, void *user_data)
+/*!
+ * \internal
+ * \brief Handle initial INVITE challenge response message.
+ * \since 13.5.0
+ *
+ * \param rdata PJSIP receive response message data.
+ *
+ * \retval PJ_FALSE Did not handle message.
+ * \retval PJ_TRUE Handled message.
+ */
+static pj_bool_t outbound_invite_auth(pjsip_rx_data *rdata)
 {
-	pjsip_inv_session *inv = pjsip_dlg_get_inv_session(dlg);
-	struct ast_sip_session *session = inv->mod_data[session_module.id];
+	pjsip_transaction *tsx;
+	pjsip_dialog *dlg;
+	pjsip_inv_session *inv;
+	pjsip_tx_data *tdata;
+	struct ast_sip_session *session;
+
+	if (rdata->msg_info.msg->line.status.code != 401
+		&& rdata->msg_info.msg->line.status.code != 407) {
+		/* Doesn't pertain to us. Move on */
+		return PJ_FALSE;
+	}
 
-	if (inv->state < PJSIP_INV_STATE_CONFIRMED && tdata->msg->line.req.method.id == PJSIP_INVITE_METHOD) {
-		pjsip_inv_uac_restart(inv, PJ_FALSE);
+	tsx = pjsip_rdata_get_tsx(rdata);
+	dlg = pjsip_rdata_get_dlg(rdata);
+	if (!dlg || !tsx) {
+		return PJ_FALSE;
 	}
+
+	if (tsx->method.id != PJSIP_INVITE_METHOD) {
+		/* Not an INVITE that needs authentication */
+		return PJ_FALSE;
+	}
+
+	inv = pjsip_dlg_get_inv_session(dlg);
+	if (PJSIP_INV_STATE_CONFIRMED <= inv->state) {
+		/*
+		 * We cannot handle reINVITE authentication at this
+		 * time because the reINVITE transaction is still in
+		 * progress.
+		 */
+		ast_debug(1, "A reINVITE is being challenged.\n");
+		return PJ_FALSE;
+	}
+	ast_debug(1, "Initial INVITE is being challenged.\n");
+
+	session = inv->mod_data[session_module.id];
+
+	if (ast_sip_create_request_with_auth(&session->endpoint->outbound_auths, rdata, tsx,
+		&tdata)) {
+		return PJ_FALSE;
+	}
+
+	/*
+	 * Restart the outgoing initial INVITE transaction to deal
+	 * with authentication.
+	 */
+	pjsip_inv_uac_restart(inv, PJ_FALSE);
+
 	ast_sip_session_send_request(session, tdata);
-	return 0;
+	return PJ_TRUE;
+}
+
+static pjsip_module outbound_invite_auth_module = {
+	.name = {"Outbound INVITE Auth", 20},
+	.priority = PJSIP_MOD_PRIORITY_DIALOG_USAGE,
+	.on_rx_response = outbound_invite_auth,
+};
+
+/*!
+ * \internal
+ * \brief Setup outbound initial INVITE authentication.
+ * \since 13.5.0
+ *
+ * \param dlg PJSIP dialog to attach outbound authentication.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int setup_outbound_invite_auth(pjsip_dialog *dlg)
+{
+	pj_status_t status;
+
+	++dlg->sess_count;
+	status = pjsip_dlg_add_usage(dlg, &outbound_invite_auth_module, NULL);
+	--dlg->sess_count;
+
+	return status != PJ_SUCCESS ? -1 : 0;
 }
 
 struct ast_sip_session *ast_sip_session_create_outgoing(struct ast_sip_endpoint *endpoint,
@@ -1252,6 +1536,7 @@ struct ast_sip_session *ast_sip_session_create_outgoing(struct ast_sip_endpoint
 	struct ast_format_cap *req_caps)
 {
 	const char *uri = NULL;
+	RAII_VAR(struct ast_sip_aor *, found_aor, NULL, ao2_cleanup);
 	RAII_VAR(struct ast_sip_contact *, found_contact, NULL, ao2_cleanup);
 	pjsip_timer_setting timer;
 	pjsip_dialog *dlg;
@@ -1262,7 +1547,7 @@ struct ast_sip_session *ast_sip_session_create_outgoing(struct ast_sip_endpoint
 	if (location || !contact) {
 		location = S_OR(location, endpoint->aors);
 
-		found_contact = ast_sip_location_retrieve_contact_from_aor_list(location);
+		ast_sip_location_retrieve_contact_and_aor_from_list(location, &found_aor, &found_contact);
 		if (!found_contact || ast_strlen_zero(found_contact->uri)) {
 			uri = location;
 		} else {
@@ -1281,7 +1566,7 @@ struct ast_sip_session *ast_sip_session_create_outgoing(struct ast_sip_endpoint
 		return NULL;
 	}
 
-	if (ast_sip_dialog_setup_outbound_authentication(dlg, endpoint, session_outbound_auth, NULL)) {
+	if (setup_outbound_invite_auth(dlg)) {
 		pjsip_dlg_terminate(dlg);
 		return NULL;
 	}
@@ -1303,6 +1588,7 @@ struct ast_sip_session *ast_sip_session_create_outgoing(struct ast_sip_endpoint
 		pjsip_inv_terminate(inv_session, 500, PJ_FALSE);
 		return NULL;
 	}
+	session->aor = ao2_bump(found_aor);
 	ast_party_id_copy(&session->id, &endpoint->id.self);
 
 	if (ast_format_cap_count(req_caps)) {
@@ -1320,7 +1606,7 @@ struct ast_sip_session *ast_sip_session_create_outgoing(struct ast_sip_endpoint
 		ao2_cleanup(joint_caps);
 	}
 
-	if ((pjsip_dlg_add_usage(dlg, &session_module, NULL) != PJ_SUCCESS)) {
+	if (pjsip_dlg_add_usage(dlg, &session_module, NULL) != PJ_SUCCESS) {
 		pjsip_inv_terminate(inv_session, 500, PJ_FALSE);
 		/* Since we are not notifying ourselves that the INVITE session is being terminated
 		 * we need to manually drop its reference to session
@@ -1333,19 +1619,58 @@ struct ast_sip_session *ast_sip_session_create_outgoing(struct ast_sip_endpoint
 	return session;
 }
 
-static int session_termination_task(void *data)
+void ast_sip_session_terminate(struct ast_sip_session *session, int response)
 {
-	RAII_VAR(struct ast_sip_session *, session, data, ao2_cleanup);
+	pj_status_t status;
 	pjsip_tx_data *packet = NULL;
 
-	if (!session->inv_session) {
-		return 0;
+	if (session->defer_terminate) {
+		session->terminate_while_deferred = 1;
+		return;
+	}
+
+	if (!response) {
+		response = 603;
+	}
+
+	if ((session->inv_session->state == PJSIP_INV_STATE_CONFIRMED) && session->inv_session->invite_tsx) {
+		ast_debug(3, "Delay sending BYE to %s because of outstanding transaction...\n",
+				ast_sorcery_object_get_id(session->endpoint));
+		/* If this is delayed the only thing that will happen is a BYE request so we don't
+		 * actually need to store the response code for when it happens.
+		 */
+		delay_request(session, NULL, NULL, NULL, 0, DELAYED_METHOD_BYE);
+	} else if (session->inv_session->state == PJSIP_INV_STATE_NULL) {
+		pjsip_inv_terminate(session->inv_session, response, PJ_TRUE);
+	} else if (((status = pjsip_inv_end_session(session->inv_session, response, NULL, &packet)) == PJ_SUCCESS)
+		&& packet) {
+		struct ast_sip_session_delayed_request *delay;
+
+		/* Flush any delayed requests so they cannot overlap this transaction. */
+		while ((delay = AST_LIST_REMOVE_HEAD(&session->delayed_requests, next))) {
+			ast_free(delay);
+		}
+
+		if (packet->msg->type == PJSIP_RESPONSE_MSG) {
+			ast_sip_session_send_response(session, packet);
+		} else {
+			ast_sip_session_send_request(session, packet);
+		}
 	}
+}
+
+static int session_termination_task(void *data)
+{
+	struct ast_sip_session *session = data;
 
-	if (pjsip_inv_end_session(session->inv_session, 603, NULL, &packet) == PJ_SUCCESS) {
-		ast_sip_session_send_request(session, packet);
+	if (session->defer_terminate) {
+		session->defer_terminate = 0;
+		if (session->inv_session) {
+			ast_sip_session_terminate(session, 0);
+		}
 	}
 
+	ao2_ref(session, -1);
 	return 0;
 }
 
@@ -1358,9 +1683,13 @@ static void session_termination_cb(pj_timer_heap_t *timer_heap, struct pj_timer_
 	}
 }
 
-void ast_sip_session_defer_termination(struct ast_sip_session *session)
+int ast_sip_session_defer_termination(struct ast_sip_session *session)
 {
 	pj_time_val delay = { .sec = 60, };
+	int res;
+
+	/* The session should not have an active deferred termination request. */
+	ast_assert(!session->defer_terminate);
 
 	session->defer_terminate = 1;
 
@@ -1369,9 +1698,47 @@ void ast_sip_session_defer_termination(struct ast_sip_session *session)
 	session->scheduled_termination.user_data = session;
 	session->scheduled_termination.cb = session_termination_cb;
 
-	if (pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(), &session->scheduled_termination, &delay) != PJ_SUCCESS) {
+	res = (pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(),
+		&session->scheduled_termination, &delay) != PJ_SUCCESS) ? -1 : 0;
+	if (res) {
+		session->defer_terminate = 0;
 		ao2_ref(session, -1);
 	}
+	return res;
+}
+
+/*!
+ * \internal
+ * \brief Stop the defer termination timer if it is still running.
+ * \since 13.5.0
+ *
+ * \param session Which session to stop the timer.
+ *
+ * \return Nothing
+ */
+static void sip_session_defer_termination_stop_timer(struct ast_sip_session *session)
+{
+	if (pj_timer_heap_cancel(pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()),
+		&session->scheduled_termination)) {
+		ao2_ref(session, -1);
+	}
+}
+
+void ast_sip_session_defer_termination_cancel(struct ast_sip_session *session)
+{
+	if (!session->defer_terminate) {
+		/* Already canceled or timer fired. */
+		return;
+	}
+	session->defer_terminate = 0;
+
+	if (session->terminate_while_deferred) {
+		/* Complete the termination started by the upper layer. */
+		ast_sip_session_terminate(session, 0);
+	}
+
+	/* Stop the termination timer if it is still running. */
+	sip_session_defer_termination_stop_timer(session);
 }
 
 struct ast_sip_session *ast_sip_dialog_get_session(pjsip_dialog *dlg)
@@ -1617,7 +1984,7 @@ static void handle_new_invite_request(pjsip_rx_data *rdata)
 			ast_pjsip_rdata_get_endpoint(rdata), ao2_cleanup);
 	pjsip_tx_data *tdata = NULL;
 	pjsip_inv_session *inv_session = NULL;
-	RAII_VAR(struct ast_sip_session *, session, NULL, ao2_cleanup);
+	struct ast_sip_session *session;
 	struct new_invite *invite;
 
 	ast_assert(endpoint != NULL);
@@ -1645,10 +2012,9 @@ static void handle_new_invite_request(pjsip_rx_data *rdata)
 		} else {
 			pjsip_inv_send_msg(inv_session, tdata);
 		}
-		ao2_ref(session, -1);
 		ao2_cleanup(invite);
-		return;
 	}
+	ao2_ref(session, -1);
 }
 
 static pj_bool_t does_method_match(const pj_str_t *message_method, const char *supplement_method)
@@ -1721,54 +2087,44 @@ static pj_bool_t session_on_rx_request(pjsip_rx_data *rdata)
 	return handled;
 }
 
-struct reschedule_reinvite_data {
-	struct ast_sip_session *session;
-	struct ast_sip_session_delayed_request *delay;
-};
-
-static struct reschedule_reinvite_data *reschedule_reinvite_data_alloc(
-		struct ast_sip_session *session, struct ast_sip_session_delayed_request *delay)
-{
-	struct reschedule_reinvite_data *rrd = ast_malloc(sizeof(*rrd));
-	if (!rrd) {
-		return NULL;
-	}
-	ao2_ref(session, +1);
-	rrd->session = session;
-	rrd->delay = delay;
-	return rrd;
-}
-
-static void reschedule_reinvite_data_destroy(struct reschedule_reinvite_data *rrd)
-{
-	ao2_cleanup(rrd->session);
-	ast_free(rrd->delay);
-	ast_free(rrd);
-}
-
-static int really_resend_reinvite(void *data)
-{
-	RAII_VAR(struct reschedule_reinvite_data *, rrd, data, reschedule_reinvite_data_destroy);
-
-	return send_delayed_request(rrd->session, rrd->delay);
-}
-
 static void resend_reinvite(pj_timer_heap_t *timer, pj_timer_entry *entry)
 {
-	struct reschedule_reinvite_data *rrd = entry->user_data;
+	struct ast_sip_session *session = entry->user_data;
 
-	ast_sip_push_task(rrd->session->serializer, really_resend_reinvite, entry->user_data);
+	ast_debug(3, "Endpoint '%s(%s)' re-INVITE collision timer expired.\n",
+		ast_sorcery_object_get_id(session->endpoint),
+		session->channel ? ast_channel_name(session->channel) : "");
+
+	if (AST_LIST_EMPTY(&session->delayed_requests)) {
+		/* No delayed request pending, so just return */
+		ao2_ref(session, -1);
+		return;
+	}
+	if (ast_sip_push_task(session->serializer, invite_collision_timeout, session)) {
+		/*
+		 * Uh oh.  We now have nothing in the foreseeable future
+		 * to trigger sending the delayed requests.
+		 */
+		ao2_ref(session, -1);
+	}
 }
 
 static void reschedule_reinvite(struct ast_sip_session *session, ast_sip_session_response_cb on_response)
 {
-	struct ast_sip_session_delayed_request *delay = delayed_request_alloc("INVITE",
-			NULL, NULL, on_response, 1);
 	pjsip_inv_session *inv = session->inv_session;
-	struct reschedule_reinvite_data *rrd = reschedule_reinvite_data_alloc(session, delay);
 	pj_time_val tv;
 
-	if (!rrd || !delay) {
+	ast_debug(3, "Endpoint '%s(%s)' re-INVITE collision.\n",
+		ast_sorcery_object_get_id(session->endpoint),
+		session->channel ? ast_channel_name(session->channel) : "");
+	if (delay_request(session, NULL, NULL, on_response, 1, DELAYED_METHOD_INVITE)) {
+		return;
+	}
+	if (pj_timer_entry_running(&session->rescheduled_reinvite)) {
+		/* Timer already running.  Something weird is going on. */
+		ast_debug(1, "Endpoint '%s(%s)' re-INVITE collision while timer running!!!\n",
+			ast_sorcery_object_get_id(session->endpoint),
+			session->channel ? ast_channel_name(session->channel) : "");
 		return;
 	}
 
@@ -1778,41 +2134,59 @@ static void reschedule_reinvite(struct ast_sip_session *session, ast_sip_session
 	} else {
 		tv.msec = ast_random() % 2000;
 	}
+	pj_timer_entry_init(&session->rescheduled_reinvite, 0, session, resend_reinvite);
 
-	pj_timer_entry_init(&session->rescheduled_reinvite, 0, rrd, resend_reinvite);
-
-	pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(), &session->rescheduled_reinvite, &tv);
+	ao2_ref(session, +1);
+	if (pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(),
+		&session->rescheduled_reinvite, &tv) != PJ_SUCCESS) {
+		ao2_ref(session, -1);
+	}
 }
 
 static void __print_debug_details(const char *function, pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e)
 {
 	struct ast_sip_session *session;
-	ast_debug(5, "Function %s called on event %s\n", function, pjsip_event_str(e->type));
+
+	if (!DEBUG_ATLEAST(5)) {
+		/* Debug not spamy enough */
+		return;
+	}
+
+	ast_log(LOG_DEBUG, "Function %s called on event %s\n",
+		function, pjsip_event_str(e->type));
 	if (!inv) {
-		ast_debug(5, "Transaction %p does not belong to an inv_session?\n", tsx);
-		ast_debug(5, "The transaction state is %s\n", pjsip_tsx_state_str(tsx->state));
+		ast_log(LOG_DEBUG, "Transaction %p does not belong to an inv_session?\n", tsx);
+		ast_log(LOG_DEBUG, "The transaction state is %s\n",
+			pjsip_tsx_state_str(tsx->state));
 		return;
 	}
 	session = inv->mod_data[session_module.id];
 	if (!session) {
-		ast_debug(5, "inv_session %p has no ast session\n", inv);
+		ast_log(LOG_DEBUG, "inv_session %p has no ast session\n", inv);
 	} else {
-		ast_debug(5, "The state change pertains to the session with %s\n",
-				ast_sorcery_object_get_id(session->endpoint));
+		ast_log(LOG_DEBUG, "The state change pertains to the endpoint '%s(%s)'\n",
+			ast_sorcery_object_get_id(session->endpoint),
+			session->channel ? ast_channel_name(session->channel) : "");
 	}
 	if (inv->invite_tsx) {
-		ast_debug(5, "The inv session still has an invite_tsx (%p)\n", inv->invite_tsx);
+		ast_log(LOG_DEBUG, "The inv session still has an invite_tsx (%p)\n",
+			inv->invite_tsx);
 	} else {
-		ast_debug(5, "The inv session does NOT have an invite_tsx\n");
+		ast_log(LOG_DEBUG, "The inv session does NOT have an invite_tsx\n");
 	}
 	if (tsx) {
-		ast_debug(5, "The transaction involved in this state change is %p\n", tsx);
-		ast_debug(5, "The current transaction state is %s\n", pjsip_tsx_state_str(tsx->state));
-		ast_debug(5, "The transaction state change event is %s\n", pjsip_event_str(e->body.tsx_state.type));
+		ast_log(LOG_DEBUG, "The %s %.*s transaction involved in this state change is %p\n",
+			pjsip_role_name(tsx->role),
+			(int) pj_strlen(&tsx->method.name), pj_strbuf(&tsx->method.name),
+			tsx);
+		ast_log(LOG_DEBUG, "The current transaction state is %s\n",
+			pjsip_tsx_state_str(tsx->state));
+		ast_log(LOG_DEBUG, "The transaction state change event is %s\n",
+			pjsip_event_str(e->body.tsx_state.type));
 	} else {
-		ast_debug(5, "There is no transaction involved in this state change\n");
+		ast_log(LOG_DEBUG, "There is no transaction involved in this state change\n");
 	}
-	ast_debug(5, "The current inv state is %s\n", pjsip_inv_state_name(inv->state));
+	ast_log(LOG_DEBUG, "The current inv state is %s\n", pjsip_inv_state_name(inv->state));
 }
 
 #define print_debug_details(inv, tsx, e) __print_debug_details(__PRETTY_FUNCTION__, (inv), (tsx), (e))
@@ -1906,25 +2280,39 @@ static void handle_outgoing(struct ast_sip_session *session, pjsip_tx_data *tdat
 	}
 }
 
-static int session_end(struct ast_sip_session *session)
+static void session_end(struct ast_sip_session *session)
 {
 	struct ast_sip_session_supplement *iter;
 
 	/* Stop the scheduled termination */
-	if (pj_timer_heap_cancel(pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()), &session->scheduled_termination)) {
-		ao2_ref(session, -1);
-	}
+	sip_session_defer_termination_stop_timer(session);
 
-	/* Session is dead. Let's get rid of the reference to the session */
+	/* Session is dead.  Notify the supplements. */
 	AST_LIST_TRAVERSE(&session->supplements, iter, next) {
 		if (iter->session_end) {
 			iter->session_end(session);
 		}
 	}
+}
+
+/*!
+ * \internal
+ * \brief Complete ending session activities.
+ * \since 13.5.0
+ *
+ * \param vsession Which session to complete stopping.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int session_end_completion(void *vsession)
+{
+	struct ast_sip_session *session = vsession;
 
-	session->inv_session->mod_data[session_module.id] = NULL;
 	ast_sip_dialog_set_serializer(session->inv_session->dlg, NULL);
 	ast_sip_dialog_set_endpoint(session->inv_session->dlg, NULL);
+
+	/* Now we can release the ref that was held by session->inv_session */
 	ao2_cleanup(session);
 	return 0;
 }
@@ -1995,11 +2383,11 @@ static void session_inv_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_trans
 {
 	ast_sip_session_response_cb cb;
 	struct ast_sip_session *session = inv->mod_data[session_module.id];
+	pjsip_tx_data *tdata;
+
 	print_debug_details(inv, tsx, e);
 	if (!session) {
-		/* Transaction likely timed out after the call was hung up. Just
-		 * ignore such transaction changes
-		 */
+		/* The session has ended.  Ignore the transaction change. */
 		return;
 	}
 	switch (e->body.tsx_state.type) {
@@ -2014,8 +2402,16 @@ static void session_inv_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_trans
 		break;
 	case PJSIP_EVENT_RX_MSG:
 		cb = ast_sip_mod_data_get(tsx->mod_data, session_module.id, MOD_DATA_ON_RESPONSE);
-		handle_incoming(session, e->body.tsx_state.src.rdata, e->type,
-				AST_SIP_SESSION_AFTER_MEDIA);
+		/* As the PJSIP invite session implementation responds with a 200 OK before we have a
+		 * chance to be invoked session supplements for BYE requests actually end up executing
+		 * in the invite session state callback as well. To prevent session supplements from
+		 * running on the BYE request again we explicitly squash invocation of them here.
+		 */
+		if ((e->body.tsx_state.src.rdata->msg_info.msg->type != PJSIP_REQUEST_MSG) ||
+			(tsx->method.id != PJSIP_BYE_METHOD)) {
+			handle_incoming(session, e->body.tsx_state.src.rdata, e->type,
+							AST_SIP_SESSION_AFTER_MEDIA);
+		}
 		if (tsx->method.id == PJSIP_INVITE_METHOD) {
 			if (tsx->role == PJSIP_ROLE_UAC) {
 				if (tsx->state == PJSIP_TSX_STATE_COMPLETED) {
@@ -2023,12 +2419,23 @@ static void session_inv_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_trans
 					if (tsx->status_code == PJSIP_SC_REQUEST_PENDING) {
 						reschedule_reinvite(session, cb);
 						return;
-					} else if (inv->state == PJSIP_INV_STATE_CONFIRMED &&
-						   tsx->status_code != 488) {
-						/* Other reinvite failures (except 488) result in destroying the session. */
-						pjsip_tx_data *tdata;
-						if (pjsip_inv_end_session(inv, 500, NULL, &tdata) == PJ_SUCCESS) {
-							ast_sip_session_send_request(session, tdata);
+					}
+					if (inv->state == PJSIP_INV_STATE_CONFIRMED) {
+						ast_debug(1, "reINVITE received final response code %d\n",
+							tsx->status_code);
+						if ((tsx->status_code == 401 || tsx->status_code == 407)
+							&& !ast_sip_create_request_with_auth(
+								&session->endpoint->outbound_auths,
+								e->body.tsx_state.src.rdata, tsx, &tdata)) {
+							/* Send authed reINVITE */
+							ast_sip_session_send_request_with_cb(session, tdata, cb);
+							return;
+						}
+						if (tsx->status_code != 488) {
+							/* Other reinvite failures (except 488) result in destroying the session. */
+							if (pjsip_inv_end_session(inv, 500, NULL, &tdata) == PJ_SUCCESS) {
+								ast_sip_session_send_request(session, tdata);
+							}
 						}
 					}
 				} else if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
@@ -2039,20 +2446,78 @@ static void session_inv_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_trans
 						 * a cancelled call. Our role is to immediately send a BYE to end the
 						 * dialog.
 						 */
-						pjsip_tx_data *tdata;
-
 						if (pjsip_inv_end_session(inv, 500, NULL, &tdata) == PJ_SUCCESS) {
 							ast_sip_session_send_request(session, tdata);
 						}
 					}
 				}
 			}
+		} else {
+			/* All other methods */
+			if (tsx->role == PJSIP_ROLE_UAC) {
+				if (tsx->state == PJSIP_TSX_STATE_COMPLETED) {
+					/* This means we got a final response to our outgoing method */
+					ast_debug(1, "%.*s received final response code %d\n",
+						(int) pj_strlen(&tsx->method.name), pj_strbuf(&tsx->method.name),
+						tsx->status_code);
+					if ((tsx->status_code == 401 || tsx->status_code == 407)
+						&& !ast_sip_create_request_with_auth(
+							&session->endpoint->outbound_auths,
+							e->body.tsx_state.src.rdata, tsx, &tdata)) {
+						/* Send authed version of the method */
+						ast_sip_session_send_request_with_cb(session, tdata, cb);
+						return;
+					}
+				}
+			}
 		}
 		if (cb) {
 			cb(session, e->body.tsx_state.src.rdata);
 		}
+		break;
 	case PJSIP_EVENT_TRANSPORT_ERROR:
+		/*
+		 * 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[session_module.id] = NULL;
+
+		if (inv->state != PJSIP_INV_STATE_DISCONNECTED) {
+			session_end(session);
+		}
+
+		/*
+		 * Pass the session ref held by session->inv_session to
+		 * session_end_completion().
+		 */
+		session_end_completion(session);
+		return;
 	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[session_module.id];
+			inv->mod_data[session_module.id] = NULL;
+			pjsip_dlg_dec_lock(inv->dlg);
+
+			/*
+			 * Pass the session ref held by session->inv_session to
+			 * session_end_completion().
+			 */
+			if (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_USER:
 	case PJSIP_EVENT_UNKNOWN:
 	case PJSIP_EVENT_TSX_STATE:
@@ -2060,13 +2525,38 @@ static void session_inv_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_trans
 		break;
 	}
 
-	/* Terminated INVITE transactions always should result in queuing delayed requests,
-	 * no matter what event caused the transaction to terminate
-	 */
-	if (tsx->method.id == PJSIP_INVITE_METHOD &&
-		((tsx->state == PJSIP_TSX_STATE_TERMINATED) ||
-		(tsx->state == PJSIP_TSX_STATE_PROCEEDING))) {
-		queue_delayed_request(session);
+	if (AST_LIST_EMPTY(&session->delayed_requests)) {
+		/* No delayed request pending, so just return */
+		return;
+	}
+
+	if (tsx->method.id == PJSIP_INVITE_METHOD) {
+		if (tsx->state == PJSIP_TSX_STATE_PROCEEDING) {
+			ast_debug(3, "Endpoint '%s(%s)' INVITE delay check. tsx-state:%s\n",
+				ast_sorcery_object_get_id(session->endpoint),
+				session->channel ? ast_channel_name(session->channel) : "",
+				pjsip_tsx_state_str(tsx->state));
+			check_delayed_requests(session, invite_proceeding);
+		} else if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
+			/*
+			 * Terminated INVITE transactions always should result in
+			 * queuing delayed requests, no matter what event caused
+			 * the transaction to terminate.
+			 */
+			ast_debug(3, "Endpoint '%s(%s)' INVITE delay check. tsx-state:%s\n",
+				ast_sorcery_object_get_id(session->endpoint),
+				session->channel ? ast_channel_name(session->channel) : "",
+				pjsip_tsx_state_str(tsx->state));
+			check_delayed_requests(session, invite_terminated);
+		}
+	} else if (tsx->role == PJSIP_ROLE_UAC
+		&& tsx->state == PJSIP_TSX_STATE_COMPLETED
+		&& !pj_strcmp2(&tsx->method.name, "UPDATE")) {
+		ast_debug(3, "Endpoint '%s(%s)' UPDATE delay check. tsx-state:%s\n",
+			ast_sorcery_object_get_id(session->endpoint),
+			session->channel ? ast_channel_name(session->channel) : "",
+			pjsip_tsx_state_str(tsx->state));
+		check_delayed_requests(session, update_completed);
 	}
 }
 
@@ -2080,9 +2570,9 @@ static int add_sdp_streams(void *obj, void *arg, void *data, int flags)
 	int res;
 
 	if (handler) {
-		/* if an already assigned handler does not handle the session_media or reports a catastrophic error, fail */
+		/* if an already assigned handler reports a catastrophic error, fail */
 		res = handler->create_outgoing_sdp_stream(session, session_media, answer);
-		if (res <= 0) {
+		if (res < 0) {
 			return 0;
 		}
 		return CMP_MATCH;
@@ -2173,12 +2663,7 @@ static struct pjmedia_sdp_session *create_local_sdp(pjsip_inv_session *inv, stru
 		if (!ast_strlen_zero(session->endpoint->media.address)) {
 			pj_strdup2(inv->pool_prov, &local->origin.addr, session->endpoint->media.address);
 		} else {
-			pj_sockaddr localaddr;
-			char our_ip[PJ_INET6_ADDRSTRLEN];
-
-			pj_gethostip(session->endpoint->media.rtp.ipv6 ? pj_AF_INET6() : pj_AF_INET(), &localaddr);
-			pj_sockaddr_print(&localaddr, our_ip, sizeof(our_ip), 0);
-			pj_strdup2(inv->pool_prov, &local->origin.addr, our_ip);
+			pj_strdup2(inv->pool_prov, &local->origin.addr, ast_sip_get_host_ip_string(session->endpoint->media.rtp.ipv6 ? pj_AF_INET6() : pj_AF_INET()));
 		}
 	}
 
@@ -2211,8 +2696,10 @@ static void session_inv_on_media_update(pjsip_inv_session *inv, pj_status_t stat
 	struct ast_sip_session *session = inv->mod_data[session_module.id];
 	const pjmedia_sdp_session *local, *remote;
 
-	if (!session->channel) {
-		/* If we don't have a channel. We really don't care about media updates.
+	if (!session || !session->channel) {
+		/*
+		 * If we don't have a session or channel then we really
+		 * don't care about media updates.
 		 * Just ignore
 		 */
 		return;
@@ -2234,6 +2721,10 @@ static pjsip_redirect_op session_inv_on_redirected(pjsip_inv_session *inv, const
 	struct ast_sip_session *session = inv->mod_data[session_module.id];
 	const pjsip_sip_uri *uri;
 
+	if (!session->channel) {
+		return PJSIP_REDIRECT_STOP;
+	}
+
 	if (session->endpoint->redirect_method == AST_SIP_REDIRECT_URI_PJSIP) {
 		return PJSIP_REDIRECT_ACCEPT;
 	}
@@ -2354,15 +2845,21 @@ static int load_module(void)
 		return AST_MODULE_LOAD_DECLINE;
 	}
 	ast_sip_register_service(&session_reinvite_module);
+	ast_sip_register_service(&outbound_invite_auth_module);
 
-	ast_module_ref(ast_module_info->self);
+	ast_module_shutdown_ref(ast_module_info->self);
 
 	return AST_MODULE_LOAD_SUCCESS;
 }
 
 static int unload_module(void)
 {
-	/* This will never get called as this module can't be unloaded */
+	ast_sip_unregister_service(&outbound_invite_auth_module);
+	ast_sip_unregister_service(&session_reinvite_module);
+	ast_sip_unregister_service(&session_module);
+	ast_sorcery_delete(ast_sip_get_sorcery(), nat_hook);
+	ao2_cleanup(nat_hook);
+	ao2_cleanup(sdp_handlers);
 	return 0;
 }
 
diff --git a/res/res_pjsip_session.exports.in b/res/res_pjsip_session.exports.in
index 07f26b7..a39485e 100644
--- a/res/res_pjsip_session.exports.in
+++ b/res/res_pjsip_session.exports.in
@@ -1,6 +1,8 @@
 {
 	global:
+		LINKER_SYMBOL_PREFIXast_sip_session_terminate;
 		LINKER_SYMBOL_PREFIXast_sip_session_defer_termination;
+		LINKER_SYMBOL_PREFIXast_sip_session_defer_termination_cancel;
 		LINKER_SYMBOL_PREFIXast_sip_session_register_sdp_handler;
 		LINKER_SYMBOL_PREFIXast_sip_session_unregister_sdp_handler;
 		LINKER_SYMBOL_PREFIXast_sip_session_register_supplement;
diff --git a/res/res_pjsip_sips_contact.c b/res/res_pjsip_sips_contact.c
new file mode 100644
index 0000000..f8e554c
--- /dev/null
+++ b/res/res_pjsip_sips_contact.c
@@ -0,0 +1,107 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2015, Digium, Inc.
+ *
+ * Mark Michelson <mmichelson 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.
+ */
+
+/*** MODULEINFO
+	<depend>pjproject</depend>
+	<depend>res_pjsip</depend>
+	<support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+#include <pjsip.h>
+
+#include "asterisk/res_pjsip.h"
+#include "asterisk/module.h"
+
+/*!
+ * \brief Upgrade Contact URIs on outgoing SIP requests to SIPS if required.
+ *
+ * The rules being used here are according to RFC 3261 section 8.1.1.8. In
+ * brief, if the request URI is SIPS or the topmost Route header is SIPS,
+ * then the Contact header we send must also be SIPS.
+ */
+static pj_status_t sips_contact_on_tx_request(pjsip_tx_data *tdata)
+{
+	pjsip_contact_hdr *contact;
+	pjsip_route_hdr *route;
+	pjsip_sip_uri *contact_uri;
+
+	contact = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, NULL);
+	if (!contact) {
+		return PJ_SUCCESS;
+	}
+
+	contact_uri = pjsip_uri_get_uri(contact->uri);
+	if (PJSIP_URI_SCHEME_IS_SIPS(contact_uri)) {
+		/* If the Contact header is already SIPS, then we don't need to do anything */
+		return PJ_SUCCESS;
+	}
+
+	if (PJSIP_URI_SCHEME_IS_SIPS(tdata->msg->line.req.uri)) {
+		ast_debug(1, "Upgrading contact URI on outgoing SIP request to SIPS due to SIPS Request URI\n");
+		pjsip_sip_uri_set_secure(contact_uri, PJ_TRUE);
+		return PJ_SUCCESS;
+	}
+
+	route = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE, NULL);
+	if (!route) {
+		return PJ_SUCCESS;
+	}
+
+	if (!PJSIP_URI_SCHEME_IS_SIPS(&route->name_addr)) {
+		return PJ_SUCCESS;
+	}
+
+	/* Our Contact header is not a SIPS URI, but our topmost Route header is. */
+	ast_debug(1, "Upgrading contact URI on outgoing SIP request to SIPS due to SIPS Route header\n");
+	pjsip_sip_uri_set_secure(contact_uri, PJ_TRUE);
+
+	return PJ_SUCCESS;
+}
+
+static pjsip_module sips_contact_module = {
+	.name = {"SIPS Contact", 12 },
+	.id = -1,
+	.priority = PJSIP_MOD_PRIORITY_TSX_LAYER - 2,
+	.on_tx_request = sips_contact_on_tx_request,
+};
+
+static int unload_module(void)
+{
+	ast_sip_unregister_service(&sips_contact_module);
+	return 0;
+}
+
+static int load_module(void)
+{
+	CHECK_PJSIP_MODULE_LOADED();
+
+	if (ast_sip_register_service(&sips_contact_module)) {
+		return AST_MODULE_LOAD_DECLINE;
+	}
+
+	return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "UAC SIPS Contact support",
+		.support_level = AST_MODULE_SUPPORT_CORE,
+		.load = load_module,
+		.unload = unload_module,
+		.load_pri = AST_MODPRI_APP_DEPEND,
+	       );
diff --git a/res/res_pjsip_t38.c b/res/res_pjsip_t38.c
index b6ef59c..2c544bb 100644
--- a/res/res_pjsip_t38.c
+++ b/res/res_pjsip_t38.c
@@ -37,7 +37,7 @@
 #include <pjmedia.h>
 #include <pjlib.h>
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 431327 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/udptl.h"
@@ -113,7 +113,11 @@ static struct t38_parameters_task_data *t38_parameters_task_data_alloc(struct as
 
 	data->session = session;
 	ao2_ref(session, +1);
-	data->frame = frame;
+	data->frame = ast_frdup(frame);
+	if (!data->frame) {
+		ao2_ref(data, -1);
+		data = NULL;
+	}
 
 	return data;
 }
@@ -131,10 +135,13 @@ static void t38_change_state(struct ast_sip_session *session, struct ast_sip_ses
 	}
 
 	session->t38state = new_state;
-	ast_debug(2, "T.38 state changed to '%u' from '%u' on channel '%s'\n", new_state, old_state, ast_channel_name(session->channel));
+	ast_debug(2, "T.38 state changed to '%u' from '%u' on channel '%s'\n",
+		new_state, old_state,
+		session->channel ? ast_channel_name(session->channel) : "<gone>");
 
 	if (pj_timer_heap_cancel(pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()), &state->timer)) {
-		ast_debug(2, "Automatic T.38 rejection on channel '%s' terminated\n", ast_channel_name(session->channel));
+		ast_debug(2, "Automatic T.38 rejection on channel '%s' terminated\n",
+			session->channel ? ast_channel_name(session->channel) : "<gone>");
 		ao2_ref(session, -1);
 	}
 
@@ -154,6 +161,9 @@ static void t38_change_state(struct ast_sip_session *session, struct ast_sip_ses
 		parameters.max_ifp = ast_udptl_get_far_max_ifp(session_media->udptl);
 		parameters.request_response = AST_T38_REQUEST_NEGOTIATE;
 		ast_udptl_set_tag(session_media->udptl, "%s", ast_channel_name(session->channel));
+
+		/* Inform the bridge the channel is in that it needs to be reconfigured */
+		ast_channel_set_unbridged(session->channel, 1);
 		break;
 	case T38_ENABLED:
 		parameters = state->their_parms;
@@ -170,7 +180,8 @@ static void t38_change_state(struct ast_sip_session *session, struct ast_sip_ses
 		}
 		break;
 	case T38_LOCAL_REINVITE:
-		/* wait until we get a peer response before responding to local reinvite */
+		/* Inform the bridge the channel is in that it needs to be reconfigured */
+		ast_channel_set_unbridged(session->channel, 1);
 		break;
 	case T38_MAX_ENUM:
 		/* Well, that shouldn't happen */
@@ -194,7 +205,8 @@ static int t38_automatic_reject(void *obj)
 		return 0;
 	}
 
-	ast_debug(2, "Automatically rejecting T.38 request on channel '%s'\n", ast_channel_name(session->channel));
+	ast_debug(2, "Automatically rejecting T.38 request on channel '%s'\n",
+		session->channel ? ast_channel_name(session->channel) : "<gone>");
 
 	t38_change_state(session, session_media, datastore->data, T38_REJECTED);
 	ast_sip_session_resume_reinvite(session);
@@ -223,9 +235,9 @@ static struct t38_state *t38_state_get_or_alloc(struct ast_sip_session *session)
 		return datastore->data;
 	}
 
-	if (!(datastore = ast_sip_session_alloc_datastore(&t38_datastore, "t38")) ||
-		!(datastore->data = ast_calloc(1, sizeof(struct t38_state))) ||
-		ast_sip_session_add_datastore(session, datastore)) {
+	if (!(datastore = ast_sip_session_alloc_datastore(&t38_datastore, "t38"))
+		|| !(datastore->data = ast_calloc(1, sizeof(struct t38_state)))
+		|| ast_sip_session_add_datastore(session, datastore)) {
 		return NULL;
 	}
 
@@ -255,6 +267,8 @@ static int t38_initialize_session(struct ast_sip_session *session, struct ast_si
 	ast_channel_set_fd(session->channel, 5, ast_udptl_fd(session_media->udptl));
 	ast_udptl_set_error_correction_scheme(session_media->udptl, session->endpoint->media.t38.error_correction);
 	ast_udptl_setnat(session_media->udptl, session->endpoint->media.t38.nat);
+	ast_udptl_set_far_max_datagram(session_media->udptl, session->endpoint->media.t38.maxdatagram);
+	ast_debug(3, "UDPTL initialized on session for %s\n", ast_channel_name(session->channel));
 
 	return 0;
 }
@@ -319,9 +333,13 @@ static int t38_interpret_parameters(void *obj)
 	case AST_T38_REQUEST_NEGOTIATE:         /* Request T38 */
 		/* Negotiation can not take place without a valid max_ifp value. */
 		if (!parameters->max_ifp) {
-			t38_change_state(data->session, session_media, state, T38_REJECTED);
 			if (data->session->t38state == T38_PEER_REINVITE) {
+				t38_change_state(data->session, session_media, state, T38_REJECTED);
 				ast_sip_session_resume_reinvite(data->session);
+			} else if (data->session->t38state == T38_ENABLED) {
+				t38_change_state(data->session, session_media, state, T38_DISABLED);
+				ast_sip_session_refresh(data->session, NULL, NULL, NULL,
+					AST_SIP_SESSION_REFRESH_METHOD_INVITE, 1);
 			}
 			break;
 		} else if (data->session->t38state == T38_PEER_REINVITE) {
@@ -396,8 +414,6 @@ static struct ast_frame *t38_framehook_write(struct ast_sip_session *session, st
 		if (ast_sip_push_task(session->serializer, t38_interpret_parameters, data)) {
 			ao2_ref(data, -1);
 		}
-
-		f = &ast_null_frame;
 	} else if (f->frametype == AST_FRAME_MODEM) {
 		RAII_VAR(struct ast_sip_session_media *, session_media, NULL, ao2_cleanup);
 
@@ -451,37 +467,75 @@ static void t38_masq(void *data, int framehook_id,
 	ast_framehook_detach(new_chan, framehook_id);
 }
 
+static int t38_consume(void *data, enum ast_frame_type type)
+{
+	return 0;
+}
+
+static const struct ast_datastore_info t38_framehook_datastore = {
+	.type = "T38 framehook",
+};
+
 /*! \brief Function called to attach T.38 framehook to channel when appropriate */
 static void t38_attach_framehook(struct ast_sip_session *session)
 {
+	int framehook_id;
+	struct ast_datastore *datastore = NULL;
 	static struct ast_framehook_interface hook = {
 		.version = AST_FRAMEHOOK_INTERFACE_VERSION,
 		.event_cb = t38_framehook,
+		.consume_cb = t38_consume,
 		.chan_fixup_cb = t38_masq,
 		.chan_breakdown_cb = t38_masq,
 	};
 
-	/* Only attach the framehook on the first outgoing INVITE or the first incoming INVITE */
-	if ((session->inv_session->state != PJSIP_INV_STATE_NULL &&
-		session->inv_session->state != PJSIP_INV_STATE_INCOMING) ||
-		!session->endpoint->media.t38.enabled) {
+	/* If the channel's already gone, bail */
+	if (!session->channel) {
+		return;
+	}
+
+	/* Only attach the framehook if t38 is enabled for the endpoint */
+	if (!session->endpoint->media.t38.enabled) {
+		return;
+	}
+
+	/* Skip attaching the framehook if the T.38 datastore already exists for the channel */
+	ast_channel_lock(session->channel);
+	if ((datastore = ast_channel_datastore_find(session->channel, &t38_framehook_datastore, NULL))) {
+		ast_channel_unlock(session->channel);
 		return;
 	}
+	ast_channel_unlock(session->channel);
 
-	if (ast_framehook_attach(session->channel, &hook) < 0) {
+	framehook_id = ast_framehook_attach(session->channel, &hook);
+	if (framehook_id < 0) {
 		ast_log(LOG_WARNING, "Could not attach T.38 Frame hook to channel, T.38 will be unavailable on '%s'\n",
 			ast_channel_name(session->channel));
+		return;
+	}
+
+	ast_channel_lock(session->channel);
+	datastore = ast_datastore_alloc(&t38_framehook_datastore, NULL);
+	if (!datastore) {
+		ast_log(LOG_ERROR, "Could not attach T.38 Frame hook to channel, T.38 will be unavailable on '%s'\n",
+			ast_channel_name(session->channel));
+		ast_framehook_detach(session->channel, framehook_id);
+		ast_channel_unlock(session->channel);
+		return;
 	}
+
+	ast_channel_datastore_add(session->channel, datastore);
+	ast_channel_unlock(session->channel);
 }
 
-/*! \brief Function called when an INVITE goes out */
+/*! \brief Function called when an INVITE arrives */
 static int t38_incoming_invite_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
 {
 	t38_attach_framehook(session);
 	return 0;
 }
 
-/*! \brief Function called when an INVITE comes in */
+/*! \brief Function called when an INVITE is sent */
 static void t38_outgoing_invite_request(struct ast_sip_session *session, struct pjsip_tx_data *tdata)
 {
 	t38_attach_framehook(session);
@@ -516,6 +570,41 @@ static struct ast_sip_session_supplement t38_supplement = {
 	.outgoing_request = t38_outgoing_invite_request,
 };
 
+static int t38_incoming_bye_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
+{
+	struct ast_datastore *datastore;
+	struct ast_sip_session_media *session_media;
+
+	if (!session->channel) {
+		return 0;
+	}
+
+	datastore = ast_sip_session_get_datastore(session, "t38");
+	if (!datastore) {
+		return 0;
+	}
+
+	session_media = ao2_find(session->media, "image", OBJ_KEY);
+	if (!session_media) {
+		ao2_ref(datastore, -1);
+		return 0;
+	}
+
+	t38_change_state(session, session_media, datastore->data, T38_REJECTED);
+
+	ao2_ref(datastore, -1);
+	ao2_ref(session_media, -1);
+
+	return 0;
+}
+
+/*! \brief Supplement for handling a remote termination of T.38 state */
+static struct ast_sip_session_supplement t38_bye_supplement = {
+	.method = "BYE",
+	.priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL + 1,
+	.incoming_request = t38_incoming_bye_request,
+};
+
 /*! \brief Parse a T.38 image stream and store the attribute information */
 static void t38_interpret_sdp(struct t38_state *state, struct ast_sip_session *session, struct ast_sip_session_media *session_media,
 	const struct pjmedia_sdp_media *stream)
@@ -551,9 +640,7 @@ static void t38_interpret_sdp(struct t38_state *state, struct ast_sip_session *s
 		} else if (!pj_stricmp2(&attr->name, "t38faxversion")) {
 			state->their_parms.version = pj_strtoul(&attr->value);
 		} else if (!pj_stricmp2(&attr->name, "t38faxmaxdatagram") || !pj_stricmp2(&attr->name, "t38maxdatagram")) {
-			if (session->endpoint->media.t38.maxdatagram) {
-				ast_udptl_set_far_max_datagram(session_media->udptl, session->endpoint->media.t38.maxdatagram);
-			} else {
+			if (!session->endpoint->media.t38.maxdatagram) {
 				ast_udptl_set_far_max_datagram(session_media->udptl, pj_strtoul(&attr->value));
 			}
 		} else if (!pj_stricmp2(&attr->name, "t38faxfillbitremoval")) {
@@ -589,10 +676,12 @@ static enum ast_sip_session_sdp_stream_defer defer_incoming_sdp_stream(
 	struct t38_state *state;
 
 	if (!session->endpoint->media.t38.enabled) {
+		ast_debug(3, "Not deferring incoming SDP stream: T.38 not enabled on %s\n", ast_channel_name(session->channel));
 		return AST_SIP_SESSION_SDP_DEFER_NOT_HANDLED;
 	}
 
 	if (t38_initialize_session(session, session_media)) {
+		ast_debug(3, "Not deferring incoming SDP stream: Failed to initialize UDPTL on %s\n", ast_channel_name(session->channel));
 		return AST_SIP_SESSION_SDP_DEFER_ERROR;
 	}
 
@@ -605,6 +694,7 @@ static enum ast_sip_session_sdp_stream_defer defer_incoming_sdp_stream(
 	/* If they are initiating the re-invite we need to defer responding until later */
 	if (session->t38state == T38_DISABLED) {
 		t38_change_state(session, session_media, state, T38_PEER_REINVITE);
+		ast_debug(3, "Deferring incoming SDP stream on %s for peer re-invite\n", ast_channel_name(session->channel));
 		return AST_SIP_SESSION_SDP_DEFER_NEEDED;
 	}
 
@@ -617,9 +707,10 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct
 {
 	struct t38_state *state;
 	char host[NI_MAXHOST];
-	RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free_ptr);
+	RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free);
 
 	if (!session->endpoint->media.t38.enabled) {
+		ast_debug(3, "Declining; T.38 not enabled on session\n");
 		return -1;
 	}
 
@@ -628,6 +719,7 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct
 	}
 
 	if ((session->t38state == T38_REJECTED) || (session->t38state == T38_DISABLED)) {
+		ast_debug(3, "Declining; T.38 state is rejected or declined\n");
 		t38_change_state(session, session_media, state, T38_DISABLED);
 		return -1;
 	}
@@ -637,6 +729,7 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct
 	/* Ensure that the address provided is valid */
 	if (ast_sockaddr_resolve(&addrs, host, PARSE_PORT_FORBID, AST_AF_INET) <= 0) {
 		/* The provided host was actually invalid so we error out this negotiation */
+		ast_debug(3, "Declining; provided host is invalid\n");
 		return -1;
 	}
 
@@ -644,6 +737,7 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct
 	if ((ast_sockaddr_is_ipv6(addrs) && !session->endpoint->media.t38.ipv6) ||
 		(ast_sockaddr_is_ipv4(addrs) && session->endpoint->media.t38.ipv6)) {
 		/* The address does not match configured */
+		ast_debug(3, "Declining, provided host does not match configured address family\n");
 		return -1;
 	}
 
@@ -666,19 +760,22 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
 	static const pj_str_t STR_T38UDPREDUNDANCY = { "t38UDPRedundancy", 16 };
 	struct t38_state *state;
 	pjmedia_sdp_media *media;
-	char hostip[PJ_INET6_ADDRSTRLEN+2];
+	const char *hostip = NULL;
 	struct ast_sockaddr addr;
 	char tmp[512];
 	pj_str_t stmp;
 
 	if (!session->endpoint->media.t38.enabled) {
+		ast_debug(3, "Not creating outgoing SDP stream: T.38 not enabled\n");
 		return 1;
 	} else if ((session->t38state != T38_LOCAL_REINVITE) && (session->t38state != T38_PEER_REINVITE) &&
 		(session->t38state != T38_ENABLED)) {
+		ast_debug(3, "Not creating outgoing SDP stream: T.38 not enabled\n");
 		return 1;
 	} else if (!(state = t38_state_get_or_alloc(session))) {
 		return -1;
 	} else if (t38_initialize_session(session, session_media)) {
+		ast_debug(3, "Not creating outgoing SDP stream: Failed to initialize T.38 session\n");
 		return -1;
 	}
 
@@ -691,14 +788,14 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
 	media->desc.transport = STR_UDPTL;
 
 	if (ast_strlen_zero(session->endpoint->media.address)) {
-		pj_sockaddr localaddr;
-
-		if (pj_gethostip(session->endpoint->media.t38.ipv6 ? pj_AF_INET6() : pj_AF_INET(), &localaddr)) {
-			return -1;
-		}
-		pj_sockaddr_print(&localaddr, hostip, sizeof(hostip), 2);
+		hostip = ast_sip_get_host_ip_string(session->endpoint->media.t38.ipv6 ? pj_AF_INET6() : pj_AF_INET());
 	} else {
-		ast_copy_string(hostip, session->endpoint->media.address, sizeof(hostip));
+		hostip = session->endpoint->media.address;
+	}
+
+	if (ast_strlen_zero(hostip)) {
+		ast_debug(3, "Not creating outgoing SDP stream: no known host IP\n");
+		return -1;
 	}
 
 	media->conn->net_type = STR_IN;
@@ -760,11 +857,12 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, struct a
 				       const struct pjmedia_sdp_session *local, const struct pjmedia_sdp_media *local_stream,
 				       const struct pjmedia_sdp_session *remote, const struct pjmedia_sdp_media *remote_stream)
 {
-	RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free_ptr);
+	RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free);
 	char host[NI_MAXHOST];
 	struct t38_state *state;
 
 	if (!session_media->udptl) {
+		ast_debug(3, "Not applying negotiated SDP stream: no UDTPL session\n");
 		return 0;
 	}
 
@@ -777,6 +875,7 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, struct a
 	/* Ensure that the address provided is valid */
 	if (ast_sockaddr_resolve(&addrs, host, PARSE_PORT_FORBID, AST_AF_UNSPEC) <= 0) {
 		/* The provided host was actually invalid so we error out this negotiation */
+		ast_debug(3, "Not applying negotiated SDP stream: failed to resolve remote stream host\n");
 		return -1;
 	}
 
@@ -835,6 +934,7 @@ static int unload_module(void)
 {
 	ast_sip_session_unregister_sdp_handler(&image_sdp_handler, "image");
 	ast_sip_session_unregister_supplement(&t38_supplement);
+	ast_sip_session_unregister_supplement(&t38_bye_supplement);
 
 	return 0;
 }
@@ -861,6 +961,11 @@ static int load_module(void)
 		goto end;
 	}
 
+	if (ast_sip_session_register_supplement(&t38_bye_supplement)) {
+		ast_log(LOG_ERROR, "Unable to register T.38 BYE session supplement\n");
+		goto end;
+	}
+
 	if (ast_sip_session_register_sdp_handler(&image_sdp_handler, "image")) {
 		ast_log(LOG_ERROR, "Unable to register SDP handler for image stream type\n");
 		goto end;
diff --git a/res/res_pjsip_transport_websocket.c b/res/res_pjsip_transport_websocket.c
index 5616715..a49eade 100644
--- a/res/res_pjsip_transport_websocket.c
+++ b/res/res_pjsip_transport_websocket.c
@@ -63,8 +63,9 @@ static pj_status_t ws_send_msg(pjsip_transport *transport,
                             pjsip_transport_callback callback)
 {
 	struct ws_transport *wstransport = (struct ws_transport *)transport;
+	uint64_t len = tdata->buf.cur - tdata->buf.start;
 
-	if (ast_websocket_write(wstransport->ws_session, AST_WEBSOCKET_OPCODE_TEXT, tdata->buf.start, (int)(tdata->buf.cur - tdata->buf.start))) {
+	if (ast_websocket_write(wstransport->ws_session, AST_WEBSOCKET_OPCODE_TEXT, tdata->buf.start, len)) {
 		return PJ_EUNKNOWN;
 	}
 
@@ -79,6 +80,25 @@ static pj_status_t ws_send_msg(pjsip_transport *transport,
 static pj_status_t ws_destroy(pjsip_transport *transport)
 {
 	struct ws_transport *wstransport = (struct ws_transport *)transport;
+	int fd = ast_websocket_fd(wstransport->ws_session);
+
+	if (fd > 0) {
+		ast_websocket_close(wstransport->ws_session, 1000);
+		shutdown(fd, SHUT_RDWR);
+	}
+
+	ao2_ref(wstransport, -1);
+
+	return PJ_SUCCESS;
+}
+
+static void transport_dtor(void *arg)
+{
+	struct ws_transport *wstransport = arg;
+
+	if (wstransport->ws_session) {
+		ast_websocket_unref(wstransport->ws_session);
+	}
 
 	if (wstransport->transport.ref_cnt) {
 		pj_atomic_destroy(wstransport->transport.ref_cnt);
@@ -88,20 +108,28 @@ static pj_status_t ws_destroy(pjsip_transport *transport)
 		pj_lock_destroy(wstransport->transport.lock);
 	}
 
-	pjsip_endpt_release_pool(wstransport->transport.endpt, wstransport->transport.pool);
+	if (wstransport->transport.endpt && wstransport->transport.pool) {
+		pjsip_endpt_release_pool(wstransport->transport.endpt, wstransport->transport.pool);
+	}
 
 	if (wstransport->rdata.tp_info.pool) {
 		pjsip_endpt_release_pool(wstransport->transport.endpt, wstransport->rdata.tp_info.pool);
 	}
-
-	return PJ_SUCCESS;
 }
 
 static int transport_shutdown(void *data)
 {
-	pjsip_transport *transport = data;
+	struct ws_transport *wstransport = data;
+
+	if (!wstransport->transport.is_shutdown && !wstransport->transport.is_destroying) {
+		pjsip_transport_shutdown(&wstransport->transport);
+	}
+
+	/* Note that the destructor calls PJSIP functions,
+	 * therefore it must be called in a PJSIP thread.
+	 */
+	ao2_ref(wstransport, -1);
 
-	pjsip_transport_shutdown(transport);
 	return 0;
 }
 
@@ -116,32 +144,45 @@ struct transport_create_data {
 static int transport_create(void *data)
 {
 	struct transport_create_data *create_data = data;
-	struct ws_transport *newtransport;
+	struct ws_transport *newtransport = NULL;
 
 	pjsip_endpoint *endpt = ast_sip_get_pjsip_endpoint();
 	struct pjsip_tpmgr *tpmgr = pjsip_endpt_get_tpmgr(endpt);
 
 	pj_pool_t *pool;
-
 	pj_str_t buf;
+	pj_status_t status;
 
-	if (!(pool = pjsip_endpt_create_pool(endpt, "ws", 512, 512))) {
-		ast_log(LOG_ERROR, "Failed to allocate WebSocket endpoint pool.\n");
-		return -1;
+	newtransport = ao2_t_alloc_options(sizeof(*newtransport), transport_dtor,
+			AO2_ALLOC_OPT_LOCK_NOLOCK, "pjsip websocket transport");
+	if (!newtransport) {
+		ast_log(LOG_ERROR, "Failed to allocate WebSocket transport.\n");
+		goto on_error;
 	}
 
-	if (!(newtransport = PJ_POOL_ZALLOC_T(pool, struct ws_transport))) {
-		ast_log(LOG_ERROR, "Failed to allocate WebSocket transport.\n");
-		pjsip_endpt_release_pool(endpt, pool);
-		return -1;
+	newtransport->transport.endpt = endpt;
+
+	if (!(pool = pjsip_endpt_create_pool(endpt, "ws", 512, 512))) {
+		ast_log(LOG_ERROR, "Failed to allocate WebSocket endpoint pool.\n");
+		goto on_error;
 	}
 
+	newtransport->transport.pool = pool;
 	newtransport->ws_session = create_data->ws_session;
 
-	pj_atomic_create(pool, 0, &newtransport->transport.ref_cnt);
-	pj_lock_create_recursive_mutex(pool, pool->obj_name, &newtransport->transport.lock);
+	/* Keep the session until transport dies */
+	ast_websocket_ref(newtransport->ws_session);
+
+	status = pj_atomic_create(pool, 0, &newtransport->transport.ref_cnt);
+	if (status != PJ_SUCCESS) {
+		goto on_error;
+	}
+
+	status = pj_lock_create_recursive_mutex(pool, pool->obj_name, &newtransport->transport.lock);
+	if (status != PJ_SUCCESS) {
+		goto on_error;
+	}
 
-	newtransport->transport.pool = pool;
 	pj_sockaddr_parse(pj_AF_UNSPEC(), 0, pj_cstr(&buf, ast_sockaddr_stringify(ast_websocket_remote_address(newtransport->ws_session))), &newtransport->transport.key.rem_addr);
 	newtransport->transport.key.rem_addr.addr.sa_family = pj_AF_INET();
 	newtransport->transport.key.type = ast_websocket_is_secure(newtransport->ws_session) ? transport_type_wss : transport_type_ws;
@@ -159,24 +200,34 @@ 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.endpt = endpt;
 	newtransport->transport.tpmgr = tpmgr;
 	newtransport->transport.send_msg = &ws_send_msg;
 	newtransport->transport.destroy = &ws_destroy;
 
-	pjsip_transport_register(newtransport->transport.tpmgr, (pjsip_transport *)newtransport);
+	status = pjsip_transport_register(newtransport->transport.tpmgr,
+			(pjsip_transport *)newtransport);
+	if (status != PJ_SUCCESS) {
+		goto on_error;
+	}
+
+	/* Add a reference for pjsip transport manager */
+	ao2_ref(newtransport, +1);
 
 	newtransport->rdata.tp_info.transport = &newtransport->transport;
 	newtransport->rdata.tp_info.pool = pjsip_endpt_create_pool(endpt, "rtd%p",
 		PJSIP_POOL_RDATA_LEN, PJSIP_POOL_RDATA_INC);
 	if (!newtransport->rdata.tp_info.pool) {
 		ast_log(LOG_ERROR, "Failed to allocate WebSocket rdata.\n");
-		pjsip_endpt_release_pool(endpt, pool);
-		return -1;
+		pjsip_transport_destroy((pjsip_transport *)newtransport);
+		goto on_error;
 	}
 
 	create_data->transport = newtransport;
 	return 0;
+
+on_error:
+	ao2_cleanup(newtransport);
+	return -1;
 }
 
 struct transport_read_data {
@@ -197,12 +248,13 @@ static int transport_read(void *data)
 	pjsip_rx_data *rdata = &newtransport->rdata;
 	int recvd;
 	pj_str_t buf;
+	int pjsip_pkt_len;
 
 	pj_gettimeofday(&rdata->pkt_info.timestamp);
 
-	pj_memcpy(rdata->pkt_info.packet, read_data->payload,
-		PJSIP_MAX_PKT_LEN < read_data->payload_len ? PJSIP_MAX_PKT_LEN : read_data->payload_len);
-	rdata->pkt_info.len = read_data->payload_len;
+	pjsip_pkt_len = PJSIP_MAX_PKT_LEN < read_data->payload_len ? PJSIP_MAX_PKT_LEN : read_data->payload_len;
+	pj_memcpy(rdata->pkt_info.packet, read_data->payload, pjsip_pkt_len);
+	rdata->pkt_info.len = pjsip_pkt_len;
 	rdata->pkt_info.zero = 0;
 
 	pj_sockaddr_parse(pj_AF_UNSPEC(), 0, pj_cstr(&buf, ast_sockaddr_stringify(ast_websocket_remote_address(session))), &rdata->pkt_info.src_addr);
@@ -329,6 +381,8 @@ static pj_bool_t websocket_on_rx_msg(pjsip_rx_data *rdata)
 
 		pj_cstr(&uri->host, rdata->pkt_info.src_name);
 		uri->port = rdata->pkt_info.src_port;
+		ast_debug(4, "Re-wrote Contact URI host/port to %.*s:%d\n",
+			(int)pj_strlen(&uri->host), pj_strbuf(&uri->host), uri->port);
 		pj_strdup(rdata->tp_info.pool, &uri->transport_param, (type == (long)transport_type_ws) ? &STR_WS : &STR_WSS);
 	}
 
@@ -365,7 +419,7 @@ static int load_module(void)
 	CHECK_PJSIP_MODULE_LOADED();
 
 	pjsip_transport_register_type(PJSIP_TRANSPORT_RELIABLE, "WS", 5060, &transport_type_ws);
-	pjsip_transport_register_type(PJSIP_TRANSPORT_RELIABLE, "WSS", 5060, &transport_type_wss);
+	pjsip_transport_register_type(PJSIP_TRANSPORT_RELIABLE | PJSIP_TRANSPORT_SECURE, "WSS", 5060, &transport_type_wss);
 
 	if (ast_sip_register_service(&websocket_module) != PJ_SUCCESS) {
 		return AST_MODULE_LOAD_DECLINE;
diff --git a/res/res_pjsip_xpidf_body_generator.c b/res/res_pjsip_xpidf_body_generator.c
index 43cb1e7..298235c 100644
--- a/res/res_pjsip_xpidf_body_generator.c
+++ b/res/res_pjsip_xpidf_body_generator.c
@@ -106,14 +106,13 @@ static void xpidf_to_string(void *body, struct ast_str **str)
 	int size;
 
 	do {
-		size = pjxpidf_print(pres, ast_str_buffer(*str), ast_str_size(*str));
-		if (size == AST_PJSIP_XML_PROLOG_LEN) {
+		size = pjxpidf_print(pres, ast_str_buffer(*str), ast_str_size(*str) - 1);
+		if (size <= AST_PJSIP_XML_PROLOG_LEN) {
 			ast_str_make_space(str, ast_str_size(*str) * 2);
 			++growths;
 		}
-	} while (size == AST_PJSIP_XML_PROLOG_LEN && growths < MAX_STRING_GROWTHS);
-
-	if (size == AST_PJSIP_XML_PROLOG_LEN) {
+	} while (size <= AST_PJSIP_XML_PROLOG_LEN && growths < MAX_STRING_GROWTHS);
+	if (size <= AST_PJSIP_XML_PROLOG_LEN) {
 		ast_log(LOG_WARNING, "XPIDF body text too large\n");
 		return;
 	}
diff --git a/res/res_pktccops.c b/res/res_pktccops.c
index b889c59..837e0f5 100644
--- a/res/res_pktccops.c
+++ b/res/res_pktccops.c
@@ -37,7 +37,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/types.h>
 #include <sys/socket.h>
@@ -799,9 +799,9 @@ static void *do_pktccops(void *data)
 				if ((idx = ast_poll_fd_index(pfds, nfds, cmts->sfd)) > -1 && (pfds[idx].revents & POLLIN)) {
 					len = cops_getmsg(cmts->sfd, recmsg);
 					if (len > 0) {
-						ast_debug(3, "COPS: got from %s:\n Header: versflag=0x%.2x opcode=%i clienttype=0x%.4x msglength=%u\n",
-							cmts->name, (unsigned)recmsg->verflag,
-							recmsg->opcode, (unsigned)recmsg->clienttype, recmsg->length);
+						ast_debug(3, "COPS: got from %s:\n Header: versflag=0x%02hhx opcode=%i clienttype=0x%04hx msglength=%u\n",
+							cmts->name, recmsg->verflag,
+							recmsg->opcode, recmsg->clienttype, recmsg->length);
 						if (recmsg->object != NULL) {
 							pobject = recmsg->object;
 							while (pobject != NULL) {
diff --git a/res/res_realtime.c b/res/res_realtime.c
index 7a5ce5b..b26cde5 100644
--- a/res/res_realtime.c
+++ b/res/res_realtime.c
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 342871 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/file.h"
 #include "asterisk/channel.h"
diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c
index 4165f3b..f6bf342 100644
--- a/res/res_rtp_asterisk.c
+++ b/res/res_rtp_asterisk.c
@@ -35,7 +35,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 425646 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/time.h>
 #include <signal.h>
@@ -204,11 +204,13 @@ struct rtp_learning_info {
 
 #ifdef HAVE_OPENSSL_SRTP
 struct dtls_details {
+	ast_mutex_t lock; /*!< Lock for timeout timer synchronization */
 	SSL *ssl;         /*!< SSL session */
 	BIO *read_bio;    /*!< Memory buffer for reading */
 	BIO *write_bio;   /*!< Memory buffer for writing */
 	enum ast_rtp_dtls_setup dtls_setup; /*!< Current setup state */
 	enum ast_rtp_dtls_connection connection; /*!< Whether this is a new or existing connection */
+	int timeout_timer; /*!< Scheduler id for timeout timer */
 };
 #endif
 
@@ -317,7 +319,6 @@ struct ast_rtp {
 
 #ifdef HAVE_OPENSSL_SRTP
 	SSL_CTX *ssl_ctx; /*!< SSL context */
-	ast_mutex_t dtls_timer_lock;           /*!< Lock for synchronization purposes */
 	enum ast_rtp_dtls_verify dtls_verify; /*!< What to verify */
 	enum ast_srtp_suite suite;   /*!< SRTP crypto suite */
 	enum ast_rtp_dtls_hash local_hash; /*!< Local hash used for the fingerprint */
@@ -326,7 +327,6 @@ struct ast_rtp {
 	unsigned char remote_fingerprint[EVP_MAX_MD_SIZE]; /*!< Fingerprint of the peer certificate */
 	unsigned int rekey; /*!< Interval at which to renegotiate and rekey */
 	int rekeyid; /*!< Scheduled item id for rekeying */
-	int dtlstimerid; /*!< Scheduled item id for DTLS retransmission for RTP */
 	struct dtls_details dtls; /*!< DTLS state information */
 #endif
 };
@@ -444,6 +444,8 @@ static int ast_rtp_sendcng(struct ast_rtp_instance *instance, int level);
 #ifdef HAVE_OPENSSL_SRTP
 static int ast_rtp_activate(struct ast_rtp_instance *instance);
 static void dtls_srtp_check_pending(struct ast_rtp_instance *instance, struct ast_rtp *rtp, int rtcp);
+static void dtls_srtp_start_timeout_timer(struct ast_rtp_instance *instance, struct ast_rtp *rtp, int rtcp);
+static void dtls_srtp_stop_timeout_timer(struct ast_rtp_instance *instance, struct ast_rtp *rtp, int rtcp);
 #endif
 
 static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int rtcp, int *ice, int use_srtp);
@@ -588,12 +590,16 @@ static int ice_reset_session(struct ast_rtp_instance *instance)
 	pj_ice_sess_role role = rtp->ice->role;
 	int res;
 
+	ast_debug(3, "Resetting ICE for RTP instance '%p'\n", instance);
 	if (!rtp->ice->is_nominating && !rtp->ice->is_complete) {
+		ast_debug(3, "Nevermind. ICE isn't ready for a reset\n");
 		return 0;
 	}
 
+	ast_debug(3, "Stopping ICE for RTP instance '%p'\n", instance);
 	ast_rtp_ice_stop(instance);
 
+	ast_debug(3, "Recreating ICE session %s (%d) for RTP instance '%p'\n", ast_sockaddr_stringify(&rtp->ice_original_rtp_addr), rtp->ice_port, instance);
 	res = ice_create(instance, &rtp->ice_original_rtp_addr, rtp->ice_port, 1);
 	if (!res) {
 		/* Preserve the role that the old ICE session used */
@@ -646,6 +652,7 @@ static void ast_rtp_ice_start(struct ast_rtp_instance *instance)
 	/* Check for equivalence in the lists */
 	if (rtp->ice_active_remote_candidates &&
 			!ice_candidates_compare(rtp->ice_proposed_remote_candidates, rtp->ice_active_remote_candidates)) {
+		ast_debug(3, "Proposed == active candidates for RTP instance '%p'\n", instance);
 		ao2_cleanup(rtp->ice_proposed_remote_candidates);
 		rtp->ice_proposed_remote_candidates = NULL;
 		return;
@@ -692,8 +699,10 @@ static void ast_rtp_ice_start(struct ast_rtp_instance *instance)
 		}
 
 		if (candidate->id == AST_RTP_ICE_COMPONENT_RTP && rtp->turn_rtp) {
+			ast_debug(3, "RTP candidate %s (%p)\n", ast_sockaddr_stringify(&candidate->address), instance);
 			pj_turn_sock_set_perm(rtp->turn_rtp, 1, &candidates[cand_cnt].addr, 1);
 		} else if (candidate->id == AST_RTP_ICE_COMPONENT_RTCP && rtp->turn_rtcp) {
+			ast_debug(3, "RTCP candidate %s (%p)\n", ast_sockaddr_stringify(&candidate->address), instance);
 			pj_turn_sock_set_perm(rtp->turn_rtcp, 1, &candidates[cand_cnt].addr, 1);
 		}
 
@@ -703,21 +712,40 @@ static void ast_rtp_ice_start(struct ast_rtp_instance *instance)
 
 	ao2_iterator_destroy(&i);
 
-	if (has_rtp && has_rtcp &&
-	    pj_ice_sess_create_check_list(rtp->ice, &ufrag, &passwd, ao2_container_count(
-						  rtp->ice_active_remote_candidates), &candidates[0]) == PJ_SUCCESS) {
-		ast_test_suite_event_notify("ICECHECKLISTCREATE", "Result: SUCCESS");
-		pj_ice_sess_start_check(rtp->ice);
-		pj_timer_heap_poll(timer_heap, NULL);
-		rtp->strict_rtp_state = STRICT_RTP_OPEN;
-		return;
+	if (cand_cnt < ao2_container_count(rtp->ice_active_remote_candidates)) {
+		ast_log(LOG_WARNING, "Lost %d ICE candidates. Consider increasing PJ_ICE_MAX_CAND in PJSIP (%p)\n",
+			ao2_container_count(rtp->ice_active_remote_candidates) - cand_cnt, instance);
+	}
+
+	if (!has_rtp) {
+		ast_log(LOG_WARNING, "No RTP candidates; skipping ICE checklist (%p)\n", instance);
+	}
+
+	if (!has_rtcp) {
+		ast_log(LOG_WARNING, "No RTCP candidates; skipping ICE checklist (%p)\n", instance);
+	}
+
+	if (has_rtp && has_rtcp) {
+		pj_status_t res = pj_ice_sess_create_check_list(rtp->ice, &ufrag, &passwd, cand_cnt, &candidates[0]);
+		char reason[80];
+
+		if (res == PJ_SUCCESS) {
+			ast_debug(3, "Successfully created ICE checklist (%p)\n", instance);
+			ast_test_suite_event_notify("ICECHECKLISTCREATE", "Result: SUCCESS");
+			pj_ice_sess_start_check(rtp->ice);
+			pj_timer_heap_poll(timer_heap, NULL);
+			rtp->strict_rtp_state = STRICT_RTP_OPEN;
+			return;
+		}
+
+		pj_strerror(res, reason, sizeof(reason));
+		ast_log(LOG_WARNING, "Failed to create ICE session check list: %s (%p)\n", reason, instance);
 	}
 
 	ast_test_suite_event_notify("ICECHECKLISTCREATE", "Result: FAILURE");
 
 	/* even though create check list failed don't stop ice as
 	   it might still work */
-	ast_debug(1, "Failed to create ICE session check list\n");
 	/* however we do need to reset remote candidates since
 	   this function may be re-entered */
 	ao2_ref(rtp->ice_active_remote_candidates, -1);
@@ -767,7 +795,11 @@ static void ast_rtp_ice_set_role(struct ast_rtp_instance *instance, enum ast_rtp
 {
 	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
 
+	ast_debug(3, "Set role to %s (%p)\n",
+		role == AST_RTP_ICE_ROLE_CONTROLLED ? "CONTROLLED" : "CONTROLLING", instance);
+
 	if (!rtp->ice) {
+		ast_debug(3, "Set role failed; no ice instance (%p)\n", instance);
 		return;
 	}
 
@@ -1229,6 +1261,8 @@ static int dtls_details_initialize(struct dtls_details *dtls, SSL_CTX *ssl_ctx,
 	}
 	dtls->connection = AST_RTP_DTLS_CONNECTION_NEW;
 
+	ast_mutex_init(&dtls->lock);
+
 	return 0;
 
 error:
@@ -1264,12 +1298,16 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con
 {
 	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
 	int res;
+#ifndef HAVE_OPENSSL_ECDH_AUTO
+	EC_KEY *ecdh;
+#endif
 
 	if (!dtls_cfg->enabled) {
 		return 0;
 	}
 
 	if (!ast_rtp_engine_srtp_is_registered()) {
+		ast_log(LOG_ERROR, "SRTP support module is not loaded or available. Try loading res_srtp.so.\n");
 		return -1;
 	}
 
@@ -1281,6 +1319,18 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con
 		return -1;
 	}
 
+	SSL_CTX_set_read_ahead(rtp->ssl_ctx, 1);
+
+#ifdef HAVE_OPENSSL_ECDH_AUTO
+	SSL_CTX_set_ecdh_auto(rtp->ssl_ctx, 1);
+#else
+	ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
+	if (ecdh) {
+		SSL_CTX_set_tmp_ecdh(rtp->ssl_ctx, ecdh);
+		EC_KEY_free(ecdh);
+	}
+#endif
+
 	rtp->dtls_verify = dtls_cfg->verify;
 
 	SSL_CTX_set_verify(rtp->ssl_ctx, (rtp->dtls_verify & AST_RTP_DTLS_VERIFY_FINGERPRINT) || (rtp->dtls_verify & AST_RTP_DTLS_VERIFY_CERTIFICATE) ?
@@ -1347,7 +1397,7 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con
 		}
 
 		for (i = 0; i < size; i++) {
-			sprintf(local_fingerprint, "%.2X:", (unsigned)fingerprint[i]);
+			sprintf(local_fingerprint, "%02hhX:", fingerprint[i]);
 			local_fingerprint += 3;
 		}
 
@@ -1394,6 +1444,8 @@ static void ast_rtp_dtls_stop(struct ast_rtp_instance *instance)
 {
 	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
 
+	dtls_srtp_stop_timeout_timer(instance, rtp, 0);
+
 	if (rtp->ssl_ctx) {
 		SSL_CTX_free(rtp->ssl_ctx);
 		rtp->ssl_ctx = NULL;
@@ -1402,11 +1454,17 @@ static void ast_rtp_dtls_stop(struct ast_rtp_instance *instance)
 	if (rtp->dtls.ssl) {
 		SSL_free(rtp->dtls.ssl);
 		rtp->dtls.ssl = NULL;
+		ast_mutex_destroy(&rtp->dtls.lock);
 	}
 
-	if (rtp->rtcp && rtp->rtcp->dtls.ssl) {
-		SSL_free(rtp->rtcp->dtls.ssl);
-		rtp->rtcp->dtls.ssl = NULL;
+	if (rtp->rtcp) {
+		dtls_srtp_stop_timeout_timer(instance, rtp, 1);
+
+		if (rtp->rtcp->dtls.ssl) {
+			SSL_free(rtp->rtcp->dtls.ssl);
+			rtp->rtcp->dtls.ssl = NULL;
+			ast_mutex_destroy(&rtp->rtcp->dtls.lock);
+		}
 	}
 }
 
@@ -1509,7 +1567,7 @@ static void ast_rtp_dtls_set_fingerprint(struct ast_rtp_instance *instance, enum
 	rtp->remote_hash = hash;
 
 	while ((value = strsep(&tmp, ":")) && (pos != (EVP_MAX_MD_SIZE - 1))) {
-		sscanf(value, "%02x", (unsigned int*)&rtp->remote_fingerprint[pos++]);
+		sscanf(value, "%02hhx", &rtp->remote_fingerprint[pos++]);
 	}
 }
 
@@ -1583,21 +1641,25 @@ static void dtls_perform_handshake(struct ast_rtp_instance *instance, struct dtl
 {
 	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
 
-	if (!dtls->ssl) {
+	/* If we are not acting as a client connecting to the remote side then
+	 * don't start the handshake as it will accomplish nothing and would conflict
+	 * with the handshake we receive from the remote side.
+	 */
+	if (!dtls->ssl || (dtls->dtls_setup != AST_RTP_DTLS_SETUP_ACTIVE)) {
 		return;
 	}
 
-	if (SSL_is_init_finished(dtls->ssl)) {
-		SSL_clear(dtls->ssl);
-		if (dtls->dtls_setup == AST_RTP_DTLS_SETUP_PASSIVE) {
-			SSL_set_accept_state(dtls->ssl);
-		} else {
-			SSL_set_connect_state(dtls->ssl);
-		}
-		dtls->connection = AST_RTP_DTLS_CONNECTION_NEW;
-	}
 	SSL_do_handshake(dtls->ssl);
+
+	/* Since the handshake is started in a thread outside of the channel thread it's possible
+	 * for the response to be handled in the channel thread before we start the timeout timer.
+	 * To ensure this doesn't actually happen we hold the DTLS lock. The channel thread will
+	 * block until we're done at which point the timeout timer will be immediately stopped.
+	 */
+	ast_mutex_lock(&dtls->lock);
 	dtls_srtp_check_pending(instance, rtp, rtcp);
+	dtls_srtp_start_timeout_timer(instance, rtp, rtcp);
+	ast_mutex_unlock(&dtls->lock);
 }
 #endif
 
@@ -1751,48 +1813,83 @@ static inline int rtcp_debug_test_addr(struct ast_sockaddr *addr)
 }
 
 #ifdef HAVE_OPENSSL_SRTP
-
-static int dtls_srtp_handle_timeout(const void *data)
+static int dtls_srtp_handle_timeout(struct ast_rtp_instance *instance, int rtcp)
 {
-	struct ast_rtp_instance *instance = (struct ast_rtp_instance *)data;
 	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+	struct dtls_details *dtls = !rtcp ? &rtp->dtls : &rtp->rtcp->dtls;
+	struct timeval dtls_timeout;
+
+	DTLSv1_handle_timeout(dtls->ssl);
+	dtls_srtp_check_pending(instance, rtp, rtcp);
 
-	if (!rtp)
-	{
+	/* If a timeout can't be retrieved then this recurring scheduled item must stop */
+	if (!DTLSv1_get_timeout(dtls->ssl, &dtls_timeout)) {
+		dtls->timeout_timer = -1;
 		return 0;
 	}
 
-	ast_mutex_lock(&rtp->dtls_timer_lock);
-	if (rtp->dtlstimerid == -1)
-	{
-		ast_mutex_unlock(&rtp->dtls_timer_lock);
+	return dtls_timeout.tv_sec * 1000 + dtls_timeout.tv_usec / 1000;
+}
+
+static int dtls_srtp_handle_rtp_timeout(const void *data)
+{
+	struct ast_rtp_instance *instance = (struct ast_rtp_instance *)data;
+	int reschedule;
+
+	reschedule = dtls_srtp_handle_timeout(instance, 0);
+
+	if (!reschedule) {
 		ao2_ref(instance, -1);
-		return 0;
 	}
 
-	rtp->dtlstimerid = -1;
-	ast_mutex_unlock(&rtp->dtls_timer_lock);
+	return reschedule;
+}
+
+static int dtls_srtp_handle_rtcp_timeout(const void *data)
+{
+	struct ast_rtp_instance *instance = (struct ast_rtp_instance *)data;
+	int reschedule;
+
+	reschedule = dtls_srtp_handle_timeout(instance, 1);
 
-	if (rtp->dtls.ssl && !SSL_is_init_finished(rtp->dtls.ssl)) {
-		DTLSv1_handle_timeout(rtp->dtls.ssl);
+	if (!reschedule) {
+		ao2_ref(instance, -1);
 	}
-	dtls_srtp_check_pending(instance, rtp, 0);
 
-	if (rtp->rtcp && rtp->rtcp->dtls.ssl && !SSL_is_init_finished(rtp->rtcp->dtls.ssl)) {
-		DTLSv1_handle_timeout(rtp->rtcp->dtls.ssl);
+	return reschedule;
+}
+
+static void dtls_srtp_start_timeout_timer(struct ast_rtp_instance *instance, struct ast_rtp *rtp, int rtcp)
+{
+	struct dtls_details *dtls = !rtcp ? &rtp->dtls : &rtp->rtcp->dtls;
+	struct timeval dtls_timeout;
+
+	if (DTLSv1_get_timeout(dtls->ssl, &dtls_timeout)) {
+		int timeout = dtls_timeout.tv_sec * 1000 + dtls_timeout.tv_usec / 1000;
+
+		ast_assert(dtls->timeout_timer == -1);
+
+		ao2_ref(instance, +1);
+		if ((dtls->timeout_timer = ast_sched_add(rtp->sched, timeout,
+			!rtcp ? dtls_srtp_handle_rtp_timeout : dtls_srtp_handle_rtcp_timeout, instance)) < 0) {
+			ao2_ref(instance, -1);
+			ast_log(LOG_WARNING, "Scheduling '%s' DTLS retransmission for RTP instance [%p] failed.\n",
+				!rtcp ? "RTP" : "RTCP", instance);
+		}
 	}
-	dtls_srtp_check_pending(instance, rtp, 1);
+}
 
-	ao2_ref(instance, -1);
+static void dtls_srtp_stop_timeout_timer(struct ast_rtp_instance *instance, struct ast_rtp *rtp, int rtcp)
+{
+	struct dtls_details *dtls = !rtcp ? &rtp->dtls : &rtp->rtcp->dtls;
 
-	return 0;
+	AST_SCHED_DEL_UNREF(rtp->sched, dtls->timeout_timer, ao2_ref(instance, -1));
 }
 
 static void dtls_srtp_check_pending(struct ast_rtp_instance *instance, struct ast_rtp *rtp, int rtcp)
 {
 	struct dtls_details *dtls = !rtcp ? &rtp->dtls : &rtp->rtcp->dtls;
 	size_t pending;
-	struct timeval dtls_timeout; /* timeout on DTLS  */
 
 	if (!dtls->ssl || !dtls->write_bio) {
 		return;
@@ -1818,24 +1915,6 @@ static void dtls_srtp_check_pending(struct ast_rtp_instance *instance, struct as
 		}
 
 		out = BIO_read(dtls->write_bio, outgoing, sizeof(outgoing));
-
-		/* Stop existing DTLS timer if running */
-		ast_mutex_lock(&rtp->dtls_timer_lock);
-		if (rtp->dtlstimerid > -1) {
-			AST_SCHED_DEL_UNREF(rtp->sched, rtp->dtlstimerid, ao2_ref(instance, -1));
-			rtp->dtlstimerid = -1;
-		}
-
-		if (DTLSv1_get_timeout(dtls->ssl, &dtls_timeout)) {
-			int timeout = dtls_timeout.tv_sec * 1000 + dtls_timeout.tv_usec / 1000;
-			ao2_ref(instance, +1);
-			if ((rtp->dtlstimerid = ast_sched_add(rtp->sched, timeout, dtls_srtp_handle_timeout, instance)) < 0) {
-				ao2_ref(instance, -1);
-				ast_log(LOG_WARNING, "scheduling DTLS retransmission for RTP instance [%p] failed.\n", instance);
-			}
-		}
-		ast_mutex_unlock(&rtp->dtls_timer_lock);
-
 		__rtp_sendto(instance, outgoing, out, 0, &remote_address, rtcp, &ice, 0);
 	}
 }
@@ -1867,6 +1946,7 @@ static int dtls_srtp_setup(struct ast_rtp *rtp, struct ast_srtp *srtp, struct as
 	unsigned char *local_key, *local_salt, *remote_key, *remote_salt;
 	struct ast_srtp_policy *local_policy, *remote_policy = NULL;
 	struct ast_rtp_instance_stats stats = { 0, };
+	int res = -1;
 
 	/* If a fingerprint is present in the SDP make sure that the peer certificate matches it */
 	if (rtp->dtls_verify & AST_RTP_DTLS_VERIFY_FINGERPRINT) {
@@ -1981,16 +2061,17 @@ static int dtls_srtp_setup(struct ast_rtp *rtp, struct ast_srtp *srtp, struct as
 		}
 	}
 
-	return 0;
+	res = 0;
 
 error:
+	/* policy->destroy() called even on success to release local reference to these resources */
 	res_srtp_policy->destroy(local_policy);
 
 	if (remote_policy) {
 		res_srtp_policy->destroy(remote_policy);
 	}
 
-	return -1;
+	return res;
 }
 #endif
 
@@ -2009,10 +2090,9 @@ static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t s
 	}
 
 #ifdef HAVE_OPENSSL_SRTP
-	dtls_srtp_check_pending(instance, rtp, rtcp);
-
-	/* If this is an SSL packet pass it to OpenSSL for processing */
-	if ((*in >= 20) && (*in <= 64)) {
+	/* If this is an SSL packet pass it to OpenSSL for processing. RFC section for first byte value:
+	 * https://tools.ietf.org/html/rfc5764#section-5.1.2 */
+	if ((*in >= 20) && (*in <= 63)) {
 		struct dtls_details *dtls = !rtcp ? &rtp->dtls : &rtp->rtcp->dtls;
 		int res = 0;
 
@@ -2023,6 +2103,15 @@ static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t s
 			return -1;
 		}
 
+		/* This mutex is locked so that this thread blocks until the dtls_perform_handshake function
+		 * completes.
+		 */
+		ast_mutex_lock(&dtls->lock);
+		ast_mutex_unlock(&dtls->lock);
+
+		/* Before we feed data into OpenSSL ensure that the timeout timer is either stopped or completed */
+		dtls_srtp_stop_timeout_timer(instance, rtp, rtcp);
+
 		/* If we don't yet know if we are active or passive and we receive a packet... we are obviously passive */
 		if (dtls->dtls_setup == AST_RTP_DTLS_SETUP_ACTPASS) {
 			dtls->dtls_setup = AST_RTP_DTLS_SETUP_PASSIVE;
@@ -2051,6 +2140,9 @@ static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t s
 				/* Use the keying material to set up key/salt information */
 				res = dtls_srtp_setup(rtp, srtp, instance);
 			}
+		} else {
+			/* Since we've sent additional traffic start the timeout timer for retransmission */
+			dtls_srtp_start_timeout_timer(instance, rtp, rtcp);
 		}
 
 		return res;
@@ -2117,6 +2209,7 @@ static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t siz
 	void *temp = buf;
 	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
 	struct ast_srtp *srtp = ast_rtp_instance_get_srtp(instance);
+	int res;
 
 	*ice = 0;
 
@@ -2135,7 +2228,11 @@ static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t siz
 	}
 #endif
 
-	return ast_sendto(rtcp ? rtp->rtcp->s : rtp->s, temp, len, flags, sa);
+	res = ast_sendto(rtcp ? rtp->rtcp->s : rtp->s, temp, len, flags, sa);
+	if (res > 0) {
+		ast_rtp_instance_set_last_tx(instance, time(NULL));
+	}
+	return res;
 }
 
 static int rtcp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int *ice)
@@ -2418,7 +2515,7 @@ static int ast_rtp_new(struct ast_rtp_instance *instance,
 	     create_new_socket("RTP",
 			       ast_sockaddr_is_ipv4(addr) ? AF_INET  :
 			       ast_sockaddr_is_ipv6(addr) ? AF_INET6 : -1)) < 0) {
-		ast_debug(1, "Failed to create a new socket for RTP instance '%p'\n", instance);
+		ast_log(LOG_WARNING, "Failed to create a new socket for RTP instance '%p'\n", instance);
 		ast_free(rtp);
 		return -1;
 	}
@@ -2459,6 +2556,7 @@ static int ast_rtp_new(struct ast_rtp_instance *instance,
 #ifdef HAVE_PJPROJECT
 	/* Create an ICE session for ICE negotiation */
 	if (icesupport) {
+		ast_debug(3, "Creating ICE session %s (%d) for RTP instance '%p'\n", ast_sockaddr_stringify(addr), x, instance);
 		if (ice_create(instance, addr, x, 0)) {
 			ast_log(LOG_NOTICE, "Failed to start ICE session\n");
 		} else {
@@ -2473,7 +2571,7 @@ static int ast_rtp_new(struct ast_rtp_instance *instance,
 
 #ifdef HAVE_OPENSSL_SRTP
 	rtp->rekeyid = -1;
-	rtp->dtlstimerid = -1;
+	rtp->dtls.timeout_timer = -1;
 #endif
 
 	rtp->f.subclass.format = ao2_bump(ast_format_none);
@@ -2491,6 +2589,10 @@ static int ast_rtp_destroy(struct ast_rtp_instance *instance)
 	struct timespec ts = { .tv_sec = wait.tv_sec, .tv_nsec = wait.tv_usec * 1000, };
 #endif
 
+#ifdef HAVE_OPENSSL_SRTP
+	ast_rtp_dtls_stop(instance);
+#endif
+
 	/* Destroy the smoother that was smoothing out audio if present */
 	if (rtp->smoother) {
 		ast_smoother_free(rtp->smoother);
@@ -2509,11 +2611,6 @@ static int ast_rtp_destroy(struct ast_rtp_instance *instance)
 		 * RTP instance while it's active.
 		 */
 		close(rtp->rtcp->s);
-#ifdef HAVE_OPENSSL_SRTP
-		if (rtp->rtcp->dtls.ssl) {
-			SSL_free(rtp->rtcp->dtls.ssl);
-		}
-#endif
 		ast_free(rtp->rtcp);
 	}
 
@@ -2565,18 +2662,6 @@ static int ast_rtp_destroy(struct ast_rtp_instance *instance)
 	}
 #endif
 
-#ifdef HAVE_OPENSSL_SRTP
-	/* Destroy the SSL context if present */
-	if (rtp->ssl_ctx) {
-		SSL_CTX_free(rtp->ssl_ctx);
-	}
-
-	/* Destroy the SSL session if present */
-	if (rtp->dtls.ssl) {
-		SSL_free(rtp->dtls.ssl);
-	}
-#endif
-
 	ao2_cleanup(rtp->lasttxformat);
 	ao2_cleanup(rtp->lastrxformat);
 	ao2_cleanup(rtp->f.subclass.format);
@@ -3054,14 +3139,16 @@ static int ast_rtcp_write_report(struct ast_rtp_instance *instance, int sr)
 			ast_verbose("  Sent packets: %u\n", rtcp_report->sender_information.packet_count);
 			ast_verbose("  Sent octets: %u\n", rtcp_report->sender_information.octet_count);
 		}
-		ast_verbose("  Report block:\n");
-		ast_verbose("    Their SSRC: %u\n", report_block->source_ssrc);
-		ast_verbose("    Fraction lost: %d\n", report_block->lost_count.fraction);
-		ast_verbose("    Cumulative loss: %u\n", report_block->lost_count.packets);
-		ast_verbose("    Highest seq no: %u\n", report_block->highest_seq_no);
-		ast_verbose("    IA jitter: %.4f\n", (double)report_block->ia_jitter / rate);
-		ast_verbose("    Their last SR: %u\n", report_block->lsr);
-		ast_verbose("    DLSR: %4.4f (sec)\n\n", (double)(report_block->dlsr / 65536.0));
+		if (report_block) {
+			ast_verbose("  Report block:\n");
+			ast_verbose("    Their SSRC: %u\n", report_block->source_ssrc);
+			ast_verbose("    Fraction lost: %d\n", report_block->lost_count.fraction);
+			ast_verbose("    Cumulative loss: %u\n", report_block->lost_count.packets);
+			ast_verbose("    Highest seq no: %u\n", report_block->highest_seq_no);
+			ast_verbose("    IA jitter: %.4f\n", (double)report_block->ia_jitter / rate);
+			ast_verbose("    Their last SR: %u\n", report_block->lsr);
+			ast_verbose("    DLSR: %4.4f (sec)\n\n", (double)(report_block->dlsr / 65536.0));
+		}
 	}
 
 	ast_rtp_instance_get_local_address(instance, &local_address);
@@ -3112,8 +3199,8 @@ static int ast_rtcp_write(const void *data)
 		/*
 		 * Not being rescheduled.
 		 */
-		ao2_ref(instance, -1);
 		rtp->rtcp->schedid = -1;
+		ao2_ref(instance, -1);
 	}
 
 	return res;
@@ -3143,10 +3230,10 @@ static int ast_rtp_raw_write(struct ast_rtp_instance *instance, struct ast_frame
 		if (ast_tvzero(frame->delivery)) {
 			/* If this isn't an absolute delivery time, Check if it is close to our prediction,
 			   and if so, go with our prediction */
-			if (abs(rtp->lastts - pred) < MAX_TIMESTAMP_SKEW) {
+			if (abs((int)rtp->lastts - pred) < MAX_TIMESTAMP_SKEW) {
 				rtp->lastts = pred;
 			} else {
-				ast_debug(3, "Difference is %d, ms is %u\n", abs(rtp->lastts - pred), ms);
+				ast_debug(3, "Difference is %d, ms is %u\n", abs((int)rtp->lastts - pred), ms);
 				mark = 1;
 			}
 		}
@@ -3157,11 +3244,11 @@ static int ast_rtp_raw_write(struct ast_rtp_instance *instance, struct ast_frame
 		rtp->lastts = rtp->lastts + ms * 90;
 		/* If it's close to our prediction, go for it */
 		if (ast_tvzero(frame->delivery)) {
-			if (abs(rtp->lastts - pred) < 7200) {
+			if (abs((int)rtp->lastts - pred) < 7200) {
 				rtp->lastts = pred;
 				rtp->lastovidtimestamp += frame->samples;
 			} else {
-				ast_debug(3, "Difference is %d, ms is %u (%u), pred/ts/samples %u/%d/%d\n", abs(rtp->lastts - pred), ms, ms * 90, rtp->lastts, pred, frame->samples);
+				ast_debug(3, "Difference is %d, ms is %u (%u), pred/ts/samples %u/%d/%d\n", abs((int)rtp->lastts - pred), ms, ms * 90, rtp->lastts, pred, frame->samples);
 				rtp->lastovidtimestamp = rtp->lastts;
 			}
 		}
@@ -3171,11 +3258,11 @@ static int ast_rtp_raw_write(struct ast_rtp_instance *instance, struct ast_frame
 		rtp->lastts = rtp->lastts + ms;
 		/* If it's close to our prediction, go for it */
 		if (ast_tvzero(frame->delivery)) {
-			if (abs(rtp->lastts - pred) < 7200) {
+			if (abs((int)rtp->lastts - pred) < 7200) {
 				rtp->lastts = pred;
 				rtp->lastotexttimestamp += frame->samples;
 			} else {
-				ast_debug(3, "Difference is %d, ms is %u, pred/ts/samples %u/%d/%d\n", abs(rtp->lastts - pred), ms, rtp->lastts, pred, frame->samples);
+				ast_debug(3, "Difference is %d, ms is %u, pred/ts/samples %u/%d/%d\n", abs((int)rtp->lastts - pred), ms, rtp->lastts, pred, frame->samples);
 				rtp->lastotexttimestamp = rtp->lastts;
 			}
 		}
@@ -3224,7 +3311,7 @@ static int ast_rtp_raw_write(struct ast_rtp_instance *instance, struct ast_frame
 			rtp->txcount++;
 			rtp->txoctetcount += (res - hdrlen);
 
-			if (rtp->rtcp && rtp->rtcp->schedid < 1) {
+			if (rtp->rtcp && rtp->rtcp->schedid < 0) {
 				ast_debug(1, "Starting RTCP transmission on RTP instance '%p'\n", instance);
 				ao2_ref(instance, +1);
 				rtp->rtcp->schedid = ast_sched_add(rtp->sched, ast_rtcp_calc_interval(rtp), ast_rtcp_write, instance);
@@ -4178,7 +4265,7 @@ static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance, unsigned int
 	ast_rtp_instance_get_remote_address(instance1, &remote_address);
 
 	if (ast_sockaddr_isnull(&remote_address)) {
-		ast_debug(1, "Remote address is null, most likely RTP has been stopped\n");
+		ast_debug(5, "Remote address is null, most likely RTP has been stopped\n");
 		return 0;
 	}
 
@@ -4332,7 +4419,8 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
 	/* If symmetric RTP is enabled see if the remote side is not what we expected and change where we are sending audio */
 	if (ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_NAT)) {
 		if (ast_sockaddr_cmp(&remote_address, &addr)) {
-			ast_rtp_instance_set_remote_address(instance, &addr);
+			/* do not update the originally given address, but only the remote */
+			ast_rtp_instance_set_incoming_source_address(instance, &addr);
 			ast_sockaddr_copy(&remote_address, &addr);
 			if (rtp->rtcp) {
 				ast_sockaddr_copy(&rtp->rtcp->them, &addr);
@@ -4434,7 +4522,7 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
 	}
 
 	/* Do not schedule RR if RTCP isn't run */
-	if (rtp->rtcp && !ast_sockaddr_isnull(&rtp->rtcp->them) && rtp->rtcp->schedid < 1) {
+	if (rtp->rtcp && !ast_sockaddr_isnull(&rtp->rtcp->them) && rtp->rtcp->schedid < 0) {
 		/* Schedule transmission of Receiver Report */
 		ao2_ref(instance, +1);
 		rtp->rtcp->schedid = ast_sched_add(rtp->sched, ast_rtcp_calc_interval(rtp), ast_rtcp_write, instance);
@@ -4460,6 +4548,10 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
 	}
 
 	payload = ast_rtp_codecs_get_payload(ast_rtp_instance_get_codecs(instance), payloadtype);
+	if (!payload) {
+		/* Unknown payload type. */
+		return AST_LIST_FIRST(&frames) ? AST_LIST_FIRST(&frames) : &ast_null_frame;
+	}
 
 	/* If the payload is not actually an Asterisk one but a special one pass it off to the respective handler */
 	if (!payload->asterisk_format) {
@@ -4486,10 +4578,7 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
 		/* Even if no frame was returned by one of the above methods,
 		 * we may have a frame to return in our frame list
 		 */
-		if (!AST_LIST_EMPTY(&frames)) {
-			return AST_LIST_FIRST(&frames);
-		}
-		return &ast_null_frame;
+		return AST_LIST_FIRST(&frames) ? AST_LIST_FIRST(&frames) : &ast_null_frame;
 	}
 
 	ao2_replace(rtp->lastrxformat, payload->format);
@@ -4614,7 +4703,7 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
 		rtp->f.delivery.tv_sec = 0;
 		rtp->f.delivery.tv_usec = 0;
 		/* Pass the RTP marker bit as bit */
-		rtp->f.subclass.frame_ending = mark;
+		rtp->f.subclass.frame_ending = mark ? 1 : 0;
 	} else if (ast_format_get_type(rtp->f.subclass.format) == AST_MEDIA_TYPE_TEXT) {
 		/* TEXT -- samples is # of samples vs. 1000 */
 		if (!rtp->lastitexttimestamp)
@@ -4684,13 +4773,14 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro
 #endif
 
 #ifdef HAVE_OPENSSL_SRTP
+			rtp->rtcp->dtls.timeout_timer = -1;
 			dtls_setup_rtcp(instance);
 #endif
 
 			return;
 		} else {
 			if (rtp->rtcp) {
-				if (rtp->rtcp->schedid > 0) {
+				if (rtp->rtcp->schedid > -1) {
 					if (!ast_sched_del(rtp->sched, rtp->rtcp->schedid)) {
 						/* Successfully cancelled scheduler entry. */
 						ao2_ref(instance, -1);
@@ -4900,12 +4990,14 @@ static void ast_rtp_stop(struct ast_rtp_instance *instance)
 
 #ifdef HAVE_OPENSSL_SRTP
 	AST_SCHED_DEL_UNREF(rtp->sched, rtp->rekeyid, ao2_ref(instance, -1));
-	ast_mutex_lock(&rtp->dtls_timer_lock);
-	AST_SCHED_DEL_UNREF(rtp->sched, rtp->dtlstimerid, ao2_ref(instance, -1));
-	ast_mutex_unlock(&rtp->dtls_timer_lock);
+
+	dtls_srtp_stop_timeout_timer(instance, rtp, 0);
+	if (rtp->rtcp) {
+		dtls_srtp_stop_timeout_timer(instance, rtp, 1);
+	}
 #endif
 
-	if (rtp->rtcp && rtp->rtcp->schedid > 0) {
+	if (rtp->rtcp && rtp->rtcp->schedid > -1) {
 		if (!ast_sched_del(rtp->sched, rtp->rtcp->schedid)) {
 			/* successfully cancelled scheduler entry. */
 			ao2_ref(instance, -1);
@@ -4984,12 +5076,33 @@ static int ast_rtp_sendcng(struct ast_rtp_instance *instance, int level)
 }
 
 #ifdef HAVE_OPENSSL_SRTP
+static void dtls_perform_setup(struct dtls_details *dtls)
+{
+	if (!dtls->ssl || !SSL_is_init_finished(dtls->ssl)) {
+		return;
+	}
+
+	SSL_clear(dtls->ssl);
+	if (dtls->dtls_setup == AST_RTP_DTLS_SETUP_PASSIVE) {
+		SSL_set_accept_state(dtls->ssl);
+	} else {
+		SSL_set_connect_state(dtls->ssl);
+	}
+	dtls->connection = AST_RTP_DTLS_CONNECTION_NEW;
+}
+
 static int ast_rtp_activate(struct ast_rtp_instance *instance)
 {
 	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
 
+	dtls_perform_setup(&rtp->dtls);
+
+	if (rtp->rtcp) {
+		dtls_perform_setup(&rtp->rtcp->dtls);
+	}
+
 	/* If ICE negotiation is enabled the DTLS Handshake will be performed upon completion of it */
-#ifdef USE_PJPROJECT
+#ifdef HAVE_PJPROJECT
 	if (rtp->ice) {
 		return 0;
 	}
diff --git a/res/res_rtp_multicast.c b/res/res_rtp_multicast.c
index e71f51a..8327cf2 100644
--- a/res/res_rtp_multicast.c
+++ b/res/res_rtp_multicast.c
@@ -34,7 +34,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/time.h>
 #include <signal.h>
diff --git a/res/res_security_log.c b/res/res_security_log.c
index 00e710b..c3fb3cf 100644
--- a/res/res_security_log.c
+++ b/res/res_security_log.c
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 400186 $");
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
 
 #include "asterisk/module.h"
 #include "asterisk/logger.h"
@@ -98,7 +98,7 @@ static void security_event_stasis_cb(struct ast_json *json)
 	event_type_json = ast_json_object_get(json, "SecurityEvent");
 	event_type = ast_json_integer_get(event_type_json);
 
-	ast_assert(event_type >= 0 && event_type < AST_SECURITY_EVENT_NUM_TYPES);
+	ast_assert((unsigned int)event_type < AST_SECURITY_EVENT_NUM_TYPES);
 
 	if (!(str = ast_str_thread_get(&security_event_buf,
 			SECURITY_EVENT_BUF_INIT_LEN))) {
@@ -152,7 +152,7 @@ static int load_module(void)
 static int unload_module(void)
 {
 	if (security_stasis_sub) {
-		security_stasis_sub = stasis_unsubscribe(security_stasis_sub);
+		security_stasis_sub = stasis_unsubscribe_and_join(security_stasis_sub);
 	}
 
 	ast_logger_unregister_level(LOG_SECURITY_NAME);
diff --git a/res/res_smdi.c b/res/res_smdi.c
index 0775aaa..7a63fe4 100644
--- a/res/res_smdi.c
+++ b/res/res_smdi.c
@@ -46,7 +46,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <termios.h>
 #include <sys/time.h>
@@ -873,10 +873,10 @@ static int smdi_md_q_cmp_fn(void *obj, void *arg, int flags)
 
 	switch (flags & OBJ_SEARCH_MASK) {
 	case OBJ_SEARCH_OBJECT:
-		if (search_msg->mesg_desk_num) {
+		if (!ast_strlen_zero(search_msg->mesg_desk_num)) {
 			cmp = strcmp(msg->mesg_desk_num, search_msg->mesg_desk_num);
 		}
-		if (search_msg->mesg_desk_term) {
+		if (!ast_strlen_zero(search_msg->mesg_desk_term)) {
 			cmp |= strcmp(msg->mesg_desk_term, search_msg->mesg_desk_term);
 		}
 		break;
diff --git a/res/res_snmp.c b/res/res_snmp.c
index 5608709..c7cfa31 100644
--- a/res/res_snmp.c
+++ b/res/res_snmp.c
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/channel.h"
 #include "asterisk/module.h"
diff --git a/res/res_sorcery_astdb.c b/res/res_sorcery_astdb.c
index 1c774af..45a5494 100644
--- a/res/res_sorcery_astdb.c
+++ b/res/res_sorcery_astdb.c
@@ -30,7 +30,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <regex.h>
 
@@ -202,6 +202,7 @@ static void *sorcery_astdb_retrieve_id(const struct ast_sorcery *sorcery, void *
 	if (ast_db_get_allocated(family, id, &value) || !(json = ast_json_load_string(value, &error)) ||
 		!(objset = sorcery_json_to_objectset(json)) || !(object = ast_sorcery_alloc(sorcery, type, id)) ||
 		ast_sorcery_objectset_apply(sorcery, object, objset)) {
+		ast_debug(3, "Failed to retrieve object '%s' from astdb\n", id);
 		ao2_cleanup(object);
 		return NULL;
 	}
diff --git a/res/res_sorcery_config.c b/res/res_sorcery_config.c
index 46b24cb..312015c 100644
--- a/res/res_sorcery_config.c
+++ b/res/res_sorcery_config.c
@@ -30,7 +30,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 425384 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <regex.h>
 
@@ -39,9 +39,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 425384 $")
 #include "asterisk/astobj2.h"
 #include "asterisk/config.h"
 #include "asterisk/uuid.h"
-
-/*! \brief Default number of buckets for sorcery objects */
-#define DEFAULT_OBJECT_BUCKETS 53
+#include "asterisk/hashtab.h"
 
 /*! \brief Structure for storing configuration file sourced objects */
 struct sorcery_config {
@@ -183,7 +181,7 @@ static void *sorcery_config_retrieve_id(const struct ast_sorcery *sorcery, void
 	struct sorcery_config *config = data;
 	RAII_VAR(struct ao2_container *, objects, ao2_global_obj_ref(config->objects), ao2_cleanup);
 
-	return objects ? ao2_find(objects, id, OBJ_KEY | OBJ_NOLOCK) : NULL;
+	return objects ? ao2_find(objects, id, OBJ_KEY) : NULL;
 }
 
 static void sorcery_config_retrieve_multiple(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const struct ast_variable *fields)
@@ -237,6 +235,7 @@ static void sorcery_config_internal_load(void *data, const struct ast_sorcery *s
 	struct ast_category *category = NULL;
 	RAII_VAR(struct ao2_container *, objects, NULL, ao2_cleanup);
 	const char *id = NULL;
+	unsigned int buckets = 0;
 
 	if (!cfg) {
 		ast_log(LOG_ERROR, "Unable to load config file '%s'\n", config->filename);
@@ -249,7 +248,37 @@ static void sorcery_config_internal_load(void *data, const struct ast_sorcery *s
 		return;
 	}
 
-	if (!(objects = ao2_container_alloc(config->buckets, sorcery_config_hash, sorcery_config_cmp))) {
+	if (!config->buckets) {
+		while ((category = ast_category_browse_filtered(cfg, NULL, category, NULL))) {
+
+			/* If given criteria has not been met skip the category, it is not applicable */
+			if (!sorcery_is_criteria_met(ast_category_first(category), config->criteria)) {
+				continue;
+			}
+
+			buckets++;
+		}
+
+		/* Determine the optimal number of buckets */
+		while (buckets && !ast_is_prime(buckets)) {
+			/* This purposely goes backwards to ensure that the container doesn't have a ton of
+			 * empty buckets for objects that will never get added.
+			 */
+			buckets--;
+		}
+
+		if (!buckets) {
+			buckets = 1;
+		}
+	} else {
+		buckets = config->buckets;
+	}
+
+	ast_debug(2, "Using bucket size of '%d' for objects of type '%s' from '%s'\n",
+		buckets, type, config->filename);
+
+	if (!(objects = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, buckets,
+		sorcery_config_hash, sorcery_config_cmp))) {
 		ast_log(LOG_ERROR, "Could not create bucket for new objects from '%s', keeping existing objects\n",
 			config->filename);
 		ast_config_destroy(cfg);
@@ -265,6 +294,18 @@ static void sorcery_config_internal_load(void *data, const struct ast_sorcery *s
 			continue;
 		}
 
+		/*  Confirm an object with this id does not already exist in the bucket.
+		 *  If it exists, however, the configuration is invalid so stop
+		 *  processing and destroy it. */
+		obj = ao2_find(objects, id, OBJ_KEY);
+
+		if (obj) {
+			ast_log(LOG_ERROR, "Config file '%s' could not be loaded; configuration contains a duplicate object: '%s' of type '%s'\n",
+				config->filename, id, type);
+			ast_config_destroy(cfg);
+			return;
+		}
+
 		if (!(obj = ast_sorcery_alloc(sorcery, type, id)) ||
 		    ast_sorcery_objectset_apply(sorcery, obj, ast_category_first(category))) {
 
@@ -284,9 +325,11 @@ static void sorcery_config_internal_load(void *data, const struct ast_sorcery *s
 			if (!(obj = sorcery_config_retrieve_id(sorcery, data, type, id))) {
 				continue;
 			}
+
+			ast_log(LOG_NOTICE, "Retaining existing configuration for object of type '%s' with id '%s'\n", type, id);
 		}
 
-		ao2_link_flags(objects, obj, OBJ_NOLOCK);
+		ao2_link(objects, obj);
 	}
 
 	ao2_global_obj_replace_unref(config->objects, objects);
@@ -305,9 +348,18 @@ static void sorcery_config_reload(void *data, const struct ast_sorcery *sorcery,
 
 static void *sorcery_config_open(const char *data)
 {
-	char *tmp = ast_strdupa(data), *filename = strsep(&tmp, ","), *option;
+	char *tmp;
+	char *filename;
+	char *option;
 	struct sorcery_config *config;
 
+	if (ast_strlen_zero(data)) {
+		return NULL;
+	}
+
+ 	tmp = ast_strdupa(data);
+ 	filename = strsep(&tmp, ",");
+
 	if (ast_strlen_zero(filename) || !(config = ao2_alloc_options(sizeof(*config) + strlen(filename) + 1, sorcery_config_destructor, AO2_ALLOC_OPT_LOCK_NOLOCK))) {
 		return NULL;
 	}
@@ -315,7 +367,6 @@ static void *sorcery_config_open(const char *data)
 	ast_uuid_generate_str(config->uuid, sizeof(config->uuid));
 
 	ast_rwlock_init(&config->objects.lock);
-	config->buckets = DEFAULT_OBJECT_BUCKETS;
 	strcpy(config->filename, filename);
 
 	while ((option = strsep(&tmp, ","))) {
@@ -323,8 +374,8 @@ static void *sorcery_config_open(const char *data)
 
 		if (!strcasecmp(name, "buckets")) {
 			if (sscanf(value, "%30u", &config->buckets) != 1) {
-				ast_log(LOG_ERROR, "Unsupported bucket size of '%s' used for configuration file '%s', defaulting to '%d'\n",
-					value, filename, DEFAULT_OBJECT_BUCKETS);
+				ast_log(LOG_ERROR, "Unsupported bucket size of '%s' used for configuration file '%s', defaulting to automatic determination\n",
+					value, filename);
 			}
 		} else if (!strcasecmp(name, "integrity")) {
 			if (!strcasecmp(value, "file")) {
diff --git a/res/res_sorcery_memory.c b/res/res_sorcery_memory.c
index 40b257e..7d398c2 100644
--- a/res/res_sorcery_memory.c
+++ b/res/res_sorcery_memory.c
@@ -30,7 +30,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <regex.h>
 
diff --git a/res/res_sorcery_memory_cache.c b/res/res_sorcery_memory_cache.c
new file mode 100644
index 0000000..99db0ce
--- /dev/null
+++ b/res/res_sorcery_memory_cache.c
@@ -0,0 +1,3509 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2015, Digium, Inc.
+ *
+ * Joshua Colp <jcolp 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 Sorcery Memory Cache Object Wizard
+ *
+ * \author Joshua Colp <jcolp at digium.com>
+ */
+
+/*** MODULEINFO
+	<support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+#include "asterisk/module.h"
+#include "asterisk/sorcery.h"
+#include "asterisk/astobj2.h"
+#include "asterisk/sched.h"
+#include "asterisk/test.h"
+#include "asterisk/heap.h"
+#include "asterisk/cli.h"
+#include "asterisk/manager.h"
+
+/*** DOCUMENTATION
+	<manager name="SorceryMemoryCacheExpireObject" language="en_US">
+		<synopsis>
+			Expire (remove) an object from a sorcery memory cache.
+		</synopsis>
+		<syntax>
+			<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+			<parameter name="Cache" required="true">
+				<para>The name of the cache to expire the object from.</para>
+			</parameter>
+			<parameter name="Object" required="true">
+				<para>The name of the object to expire.</para>
+			</parameter>
+		</syntax>
+		<description>
+			<para>Expires (removes) an object from a sorcery memory cache.</para>
+		</description>
+	</manager>
+	<manager name="SorceryMemoryCacheExpire" language="en_US">
+		<synopsis>
+			Expire (remove) ALL objects from a sorcery memory cache.
+		</synopsis>
+		<syntax>
+			<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+			<parameter name="Cache" required="true">
+				<para>The name of the cache to expire all objects from.</para>
+			</parameter>
+		</syntax>
+		<description>
+			<para>Expires (removes) ALL objects from a sorcery memory cache.</para>
+		</description>
+	</manager>
+	<manager name="SorceryMemoryCacheStaleObject" language="en_US">
+		<synopsis>
+			Mark an object in a sorcery memory cache as stale.
+		</synopsis>
+		<syntax>
+			<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+			<parameter name="Cache" required="true">
+				<para>The name of the cache to mark the object as stale in.</para>
+			</parameter>
+			<parameter name="Object" required="true">
+				<para>The name of the object to mark as stale.</para>
+			</parameter>
+		</syntax>
+		<description>
+			<para>Marks an object as stale within a sorcery memory cache.</para>
+		</description>
+	</manager>
+	<manager name="SorceryMemoryCacheStale" language="en_US">
+		<synopsis>
+			Marks ALL objects in a sorcery memory cache as stale.
+		</synopsis>
+		<syntax>
+			<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+			<parameter name="Cache" required="true">
+				<para>The name of the cache to mark all object as stale in.</para>
+			</parameter>
+		</syntax>
+		<description>
+			<para>Marks ALL objects in a sorcery memory cache as stale.</para>
+		</description>
+	</manager>
+	<manager name="SorceryMemoryCachePopulate" language="en_US">
+		<synopsis>
+			Expire all objects from a memory cache and populate it with all objects from the backend.
+		</synopsis>
+		<syntax>
+			<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+			<parameter name="Cache" required="true">
+				<para>The name of the cache to populate.</para>
+			</parameter>
+		</syntax>
+		<description>
+			<para>Expires all objects from a memory cache and populate it with all objects from the backend.</para>
+		</description>
+	</manager>
+ ***/
+
+/*! \brief Structure for storing a memory cache */
+struct sorcery_memory_cache {
+	/*! \brief The name of the memory cache */
+	char *name;
+	/*! \brief Objects in the cache */
+	struct ao2_container *objects;
+	/*! \brief The maximum number of objects permitted in the cache, 0 if no limit */
+	unsigned int maximum_objects;
+	/*! \brief The maximum time (in seconds) an object will stay in the cache, 0 if no limit */
+	unsigned int object_lifetime_maximum;
+	/*! \brief The amount of time (in seconds) before an object is marked as stale, 0 if disabled */
+	unsigned int object_lifetime_stale;
+	/*! \brief Whether all objects are expired when the object type is reloaded, 0 if disabled */
+	unsigned int expire_on_reload;
+	/*! \brief Whether this is a cache of the entire backend, 0 if disabled */
+	unsigned int full_backend_cache;
+	/*! \brief Heap of cached objects. Oldest object is at the top. */
+	struct ast_heap *object_heap;
+	/*! \brief Scheduler item for expiring oldest object. */
+	int expire_id;
+	/*! \brief scheduler id of stale update task */
+	int stale_update_sched_id;
+	/*! \brief An unreffed pointer to the sorcery instance, accessible only with lock held */
+	const struct ast_sorcery *sorcery;
+	/*! \brief The type of object we are caching */
+	char *object_type;
+	/*! TRUE if trying to stop the oldest object expiration scheduler item. */
+	unsigned int del_expire:1;
+#ifdef TEST_FRAMEWORK
+	/*! \brief Variable used to indicate we should notify a test when we reach empty */
+	unsigned int cache_notify;
+	/*! \brief Mutex lock used for signaling when the cache has reached empty */
+	ast_mutex_t lock;
+	/*! \brief Condition used for signaling when the cache has reached empty */
+	ast_cond_t cond;
+	/*! \brief Variable that is set when the cache has reached empty */
+	unsigned int cache_completed;
+#endif
+};
+
+/*! \brief Structure for stored a cached object */
+struct sorcery_memory_cached_object {
+	/*! \brief The cached object */
+	void *object;
+	/*! \brief The time at which the object was created */
+	struct timeval created;
+	/*! \brief index required by heap */
+	ssize_t __heap_index;
+	/*! \brief scheduler id of stale update task */
+	int stale_update_sched_id;
+	/*! \brief Cached objectset for field and regex retrieval */
+	struct ast_variable *objectset;
+};
+
+/*! \brief Structure used for fields comparison */
+struct sorcery_memory_cache_fields_cmp_params {
+	/*! \brief Pointer to the sorcery structure */
+	const struct ast_sorcery *sorcery;
+	/*! \brief The sorcery memory cache */
+	struct sorcery_memory_cache *cache;
+	/*! \brief Pointer to the fields to check */
+	const struct ast_variable *fields;
+	/*! \brief Regular expression for checking object id */
+	regex_t *regex;
+	/*! \brief Optional container to put object into */
+	struct ao2_container *container;
+};
+
+static void *sorcery_memory_cache_open(const char *data);
+static int sorcery_memory_cache_create(const struct ast_sorcery *sorcery, void *data, void *object);
+static void sorcery_memory_cache_load(void *data, const struct ast_sorcery *sorcery, const char *type);
+static void sorcery_memory_cache_reload(void *data, const struct ast_sorcery *sorcery, const char *type);
+static void *sorcery_memory_cache_retrieve_id(const struct ast_sorcery *sorcery, void *data, const char *type,
+	const char *id);
+static void *sorcery_memory_cache_retrieve_fields(const struct ast_sorcery *sorcery, void *data, const char *type,
+	const struct ast_variable *fields);
+static void sorcery_memory_cache_retrieve_multiple(const struct ast_sorcery *sorcery, void *data, const char *type,
+	struct ao2_container *objects, const struct ast_variable *fields);
+static void sorcery_memory_cache_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type,
+	struct ao2_container *objects, const char *regex);
+static int sorcery_memory_cache_delete(const struct ast_sorcery *sorcery, void *data, void *object);
+static void sorcery_memory_cache_close(void *data);
+
+static struct ast_sorcery_wizard memory_cache_object_wizard = {
+	.name = "memory_cache",
+	.open = sorcery_memory_cache_open,
+	.create = sorcery_memory_cache_create,
+	.update = sorcery_memory_cache_create,
+	.delete = sorcery_memory_cache_delete,
+	.load = sorcery_memory_cache_load,
+	.reload = sorcery_memory_cache_reload,
+	.retrieve_id = sorcery_memory_cache_retrieve_id,
+	.retrieve_fields = sorcery_memory_cache_retrieve_fields,
+	.retrieve_multiple = sorcery_memory_cache_retrieve_multiple,
+	.retrieve_regex = sorcery_memory_cache_retrieve_regex,
+	.close = sorcery_memory_cache_close,
+};
+
+/*! \brief The bucket size for the container of caches */
+#define CACHES_CONTAINER_BUCKET_SIZE 53
+
+/*! \brief The default bucket size for the container of objects in the cache */
+#define CACHE_CONTAINER_BUCKET_SIZE 53
+
+/*! \brief Height of heap for cache object heap. Allows 31 initial objects */
+#define CACHE_HEAP_INIT_HEIGHT 5
+
+/*! \brief Container of created caches */
+static struct ao2_container *caches;
+
+/*! \brief Scheduler for cache management */
+static struct ast_sched_context *sched;
+
+#define PASSTHRU_UPDATE_THREAD_ID 0x5EED1E55
+AST_THREADSTORAGE(passthru_update_id_storage);
+
+static int is_passthru_update(void)
+{
+	uint32_t *passthru_update_thread_id;
+
+	passthru_update_thread_id = ast_threadstorage_get(&passthru_update_id_storage,
+		sizeof(*passthru_update_thread_id));
+	if (!passthru_update_thread_id) {
+		return 0;
+	}
+
+	return *passthru_update_thread_id == PASSTHRU_UPDATE_THREAD_ID;
+}
+
+static void set_passthru_update(uint32_t value)
+{
+	uint32_t *passthru_update_thread_id;
+
+	passthru_update_thread_id = ast_threadstorage_get(&passthru_update_id_storage,
+		sizeof(*passthru_update_thread_id));
+	if (!passthru_update_thread_id) {
+		ast_log(LOG_ERROR, "Could not set passthru update ID for sorcery memory cache thread\n");
+		return;
+	}
+
+	*passthru_update_thread_id = value;
+}
+
+static void start_passthru_update(void)
+{
+	set_passthru_update(PASSTHRU_UPDATE_THREAD_ID);
+}
+
+static void end_passthru_update(void)
+{
+	set_passthru_update(0);
+}
+
+/*!
+ * \internal
+ * \brief Hashing function for the container holding caches
+ *
+ * \param obj A sorcery memory cache or name of one
+ * \param flags Hashing flags
+ *
+ * \return The hash of the memory cache name
+ */
+static int sorcery_memory_cache_hash(const void *obj, int flags)
+{
+	const struct sorcery_memory_cache *cache = obj;
+	const char *name = obj;
+	int hash;
+
+	switch (flags & OBJ_SEARCH_MASK) {
+	default:
+	case OBJ_SEARCH_OBJECT:
+		name = cache->name;
+		/* Fall through */
+	case OBJ_SEARCH_KEY:
+		hash = ast_str_hash(name);
+		break;
+	case OBJ_SEARCH_PARTIAL_KEY:
+		/* Should never happen in hash callback. */
+		ast_assert(0);
+		hash = 0;
+		break;
+	}
+	return hash;
+}
+
+/*!
+ * \internal
+ * \brief Comparison function for the container holding caches
+ *
+ * \param obj A sorcery memory cache
+ * \param arg A sorcery memory cache, or name of one
+ * \param flags Comparison flags
+ *
+ * \retval CMP_MATCH if the name is the same
+ * \retval 0 if the name does not match
+ */
+static int sorcery_memory_cache_cmp(void *obj, void *arg, int flags)
+{
+	const struct sorcery_memory_cache *left = obj;
+	const struct sorcery_memory_cache *right = arg;
+	const char *right_name = arg;
+	int cmp;
+
+	switch (flags & OBJ_SEARCH_MASK) {
+	default:
+	case OBJ_SEARCH_OBJECT:
+		right_name = right->name;
+		/* Fall through */
+	case OBJ_SEARCH_KEY:
+		cmp = strcmp(left->name, right_name);
+		break;
+	case OBJ_SEARCH_PARTIAL_KEY:
+		cmp = strncmp(left->name, right_name, strlen(right_name));
+		break;
+	}
+	return cmp ? 0 : CMP_MATCH;
+}
+
+/*!
+ * \internal
+ * \brief Hashing function for the container holding cached objects
+ *
+ * \param obj A cached object or id of one
+ * \param flags Hashing flags
+ *
+ * \return The hash of the cached object id
+ */
+static int sorcery_memory_cached_object_hash(const void *obj, int flags)
+{
+	const struct sorcery_memory_cached_object *cached = obj;
+	const char *name = obj;
+	int hash;
+
+	switch (flags & OBJ_SEARCH_MASK) {
+	default:
+	case OBJ_SEARCH_OBJECT:
+		name = ast_sorcery_object_get_id(cached->object);
+		/* Fall through */
+	case OBJ_SEARCH_KEY:
+		hash = ast_str_hash(name);
+		break;
+	case OBJ_SEARCH_PARTIAL_KEY:
+		/* Should never happen in hash callback. */
+		ast_assert(0);
+		hash = 0;
+		break;
+	}
+	return hash;
+}
+
+/*!
+ * \internal
+ * \brief Comparison function for the container holding cached objects
+ *
+ * \param obj A cached object
+ * \param arg A cached object, or id of one
+ * \param flags Comparison flags
+ *
+ * \retval CMP_MATCH if the id is the same
+ * \retval 0 if the id does not match
+ */
+static int sorcery_memory_cached_object_cmp(void *obj, void *arg, int flags)
+{
+	struct sorcery_memory_cached_object *left = obj;
+	struct sorcery_memory_cached_object *right = arg;
+	const char *right_name = arg;
+	int cmp;
+
+	switch (flags & OBJ_SEARCH_MASK) {
+	default:
+	case OBJ_SEARCH_OBJECT:
+		right_name = ast_sorcery_object_get_id(right->object);
+		/* Fall through */
+	case OBJ_SEARCH_KEY:
+		cmp = strcmp(ast_sorcery_object_get_id(left->object), right_name);
+		break;
+	case OBJ_SEARCH_PARTIAL_KEY:
+		cmp = strncmp(ast_sorcery_object_get_id(left->object), right_name, strlen(right_name));
+		break;
+	}
+	return cmp ? 0 : CMP_MATCH;
+}
+
+/*!
+ * \internal
+ * \brief Destructor function for a sorcery memory cache
+ *
+ * \param obj A sorcery memory cache
+ */
+static void sorcery_memory_cache_destructor(void *obj)
+{
+	struct sorcery_memory_cache *cache = obj;
+
+	ast_free(cache->name);
+	if (cache->object_heap) {
+		ast_heap_destroy(cache->object_heap);
+	}
+	ao2_cleanup(cache->objects);
+	ast_free(cache->object_type);
+}
+
+/*!
+ * \internal
+ * \brief Destructor function for sorcery memory cached objects
+ *
+ * \param obj A sorcery memory cached object
+ */
+static void sorcery_memory_cached_object_destructor(void *obj)
+{
+	struct sorcery_memory_cached_object *cached = obj;
+
+	ao2_cleanup(cached->object);
+	ast_variables_destroy(cached->objectset);
+}
+
+static int schedule_cache_expiration(struct sorcery_memory_cache *cache);
+
+/*!
+ * \internal
+ * \brief Remove an object from the cache.
+ *
+ * This removes the item from both the hashtable and the heap.
+ *
+ * \pre cache->objects is write-locked
+ *
+ * \param cache The cache from which the object is being removed.
+ * \param id The sorcery object id of the object to remove.
+ * \param reschedule Reschedule cache expiration if this was the oldest object.
+ *
+ * \retval 0 Success
+ * \retval non-zero Failure
+ */
+static int remove_from_cache(struct sorcery_memory_cache *cache, const char *id, int reschedule)
+{
+	struct sorcery_memory_cached_object *hash_object;
+	struct sorcery_memory_cached_object *oldest_object;
+	struct sorcery_memory_cached_object *heap_object;
+
+	hash_object = ao2_find(cache->objects, id, OBJ_SEARCH_KEY | OBJ_UNLINK | OBJ_NOLOCK);
+	if (!hash_object) {
+		return -1;
+	}
+
+	ast_assert(!strcmp(ast_sorcery_object_get_id(hash_object->object), id));
+
+	oldest_object = ast_heap_peek(cache->object_heap, 1);
+	heap_object = ast_heap_remove(cache->object_heap, hash_object);
+
+	ast_assert(heap_object == hash_object);
+
+	ao2_ref(hash_object, -1);
+
+	if (reschedule && (oldest_object == heap_object)) {
+		schedule_cache_expiration(cache);
+	}
+
+	return 0;
+}
+
+/*!
+ * \internal
+ * \brief Scheduler callback invoked to expire old objects
+ *
+ * \param data The opaque callback data (in our case, the memory cache)
+ */
+static int expire_objects_from_cache(const void *data)
+{
+	struct sorcery_memory_cache *cache = (struct sorcery_memory_cache *)data;
+	struct sorcery_memory_cached_object *cached;
+
+	/*
+	 * We need to do deadlock avoidance between a non-scheduler thread
+	 * blocking when trying to delete the scheduled entry for this
+	 * callback because the scheduler thread is running this callback
+	 * and this callback waiting for the cache->objects container lock
+	 * that the blocked non-scheduler thread already holds.
+	 */
+	while (ao2_trywrlock(cache->objects)) {
+		if (cache->del_expire) {
+			cache->expire_id = -1;
+			ao2_ref(cache, -1);
+			return 0;
+		}
+		sched_yield();
+	}
+
+	cache->expire_id = -1;
+
+	/* This is an optimization for objects which have been cached close to each other */
+	while ((cached = ast_heap_peek(cache->object_heap, 1))) {
+		int expiration;
+
+		expiration = ast_tvdiff_ms(ast_tvadd(cached->created, ast_samp2tv(cache->object_lifetime_maximum, 1)), ast_tvnow());
+
+		/* If the current oldest object has not yet expired stop and reschedule for it */
+		if (expiration > 0) {
+			break;
+		}
+
+		remove_from_cache(cache, ast_sorcery_object_get_id(cached->object), 0);
+	}
+
+	schedule_cache_expiration(cache);
+
+	ao2_unlock(cache->objects);
+
+	ao2_ref(cache, -1);
+
+	return 0;
+}
+
+/*!
+ * \internal
+ * \brief Remove all objects from the cache.
+ *
+ * This removes ALL objects from both the hash table and heap.
+ *
+ * \pre cache->objects is write-locked
+ *
+ * \param cache The cache to empty.
+ */
+static void remove_all_from_cache(struct sorcery_memory_cache *cache)
+{
+	while (ast_heap_pop(cache->object_heap)) {
+	}
+
+	ao2_callback(cache->objects, OBJ_UNLINK | OBJ_NOLOCK | OBJ_NODATA | OBJ_MULTIPLE,
+		NULL, NULL);
+
+	cache->del_expire = 1;
+	AST_SCHED_DEL_UNREF(sched, cache->expire_id, ao2_ref(cache, -1));
+	cache->del_expire = 0;
+}
+
+/*!
+ * \internal
+ * \brief AO2 callback function for making an object stale immediately
+ *
+ * This changes the creation time of an object so it appears as though it is stale immediately.
+ *
+ * \param obj The cached object
+ * \param arg The cache itself
+ * \param flags Unused flags
+ */
+static int object_stale_callback(void *obj, void *arg, int flags)
+{
+	struct sorcery_memory_cached_object *cached = obj;
+	struct sorcery_memory_cache *cache = arg;
+
+	/* Since our granularity is seconds it's possible for something to retrieve us within a window
+	 * where we wouldn't be treated as stale. To ensure that doesn't happen we use the configured stale
+	 * time plus a second.
+	 */
+	cached->created = ast_tvsub(cached->created, ast_samp2tv(cache->object_lifetime_stale + 1, 1));
+
+	return CMP_MATCH;
+}
+
+/*!
+ * \internal
+ * \brief Mark an object as stale explicitly.
+ *
+ * This changes the creation time of an object so it appears as though it is stale immediately.
+ *
+ * \pre cache->objects is read-locked
+ *
+ * \param cache The cache the object is in
+ * \param id The unique identifier of the object
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+static int mark_object_as_stale_in_cache(struct sorcery_memory_cache *cache, const char *id)
+{
+	struct sorcery_memory_cached_object *cached;
+
+	cached = ao2_find(cache->objects, id, OBJ_SEARCH_KEY | OBJ_NOLOCK);
+	if (!cached) {
+		return -1;
+	}
+
+	ast_assert(!strcmp(ast_sorcery_object_get_id(cached->object), id));
+
+	object_stale_callback(cached, cache, 0);
+	ao2_ref(cached, -1);
+
+	return 0;
+}
+
+/*!
+ * \internal
+ * \brief Mark all objects as stale within a cache.
+ *
+ * This changes the creation time of ALL objects so they appear as though they are stale.
+ *
+ * \pre cache->objects is read-locked
+ *
+ * \param cache
+ */
+static void mark_all_as_stale_in_cache(struct sorcery_memory_cache *cache)
+{
+	ao2_callback(cache->objects, OBJ_NOLOCK | OBJ_NODATA | OBJ_MULTIPLE, object_stale_callback, cache);
+}
+
+/*!
+ * \internal
+ * \brief Schedule a callback for cached object expiration.
+ *
+ * \pre cache->objects is write-locked
+ *
+ * \param cache The cache that is having its callback scheduled.
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+static int schedule_cache_expiration(struct sorcery_memory_cache *cache)
+{
+	struct sorcery_memory_cached_object *cached;
+	int expiration = 0;
+
+	if (!cache->object_lifetime_maximum) {
+		return 0;
+	}
+
+	cache->del_expire = 1;
+	AST_SCHED_DEL_UNREF(sched, cache->expire_id, ao2_ref(cache, -1));
+	cache->del_expire = 0;
+
+	cached = ast_heap_peek(cache->object_heap, 1);
+	if (!cached) {
+#ifdef TEST_FRAMEWORK
+		ast_mutex_lock(&cache->lock);
+		cache->cache_completed = 1;
+		ast_cond_signal(&cache->cond);
+		ast_mutex_unlock(&cache->lock);
+#endif
+		return 0;
+	}
+
+	expiration = MAX(ast_tvdiff_ms(ast_tvadd(cached->created, ast_samp2tv(cache->object_lifetime_maximum, 1)), ast_tvnow()),
+		1);
+
+	cache->expire_id = ast_sched_add(sched, expiration, expire_objects_from_cache, ao2_bump(cache));
+	if (cache->expire_id < 0) {
+		ao2_ref(cache, -1);
+		return -1;
+	}
+
+	return 0;
+}
+
+/*!
+ * \internal
+ * \brief Remove the oldest item from the cache.
+ *
+ * \pre cache->objects is write-locked
+ *
+ * \param cache The cache from which to remove the oldest object
+ *
+ * \retval 0 Success
+ * \retval non-zero Failure
+ */
+static int remove_oldest_from_cache(struct sorcery_memory_cache *cache)
+{
+	struct sorcery_memory_cached_object *heap_old_object;
+	struct sorcery_memory_cached_object *hash_old_object;
+
+	heap_old_object = ast_heap_pop(cache->object_heap);
+	if (!heap_old_object) {
+		return -1;
+	}
+	hash_old_object = ao2_find(cache->objects, heap_old_object,
+		OBJ_SEARCH_OBJECT | OBJ_UNLINK | OBJ_NOLOCK);
+
+	ast_assert(heap_old_object == hash_old_object);
+
+	ao2_ref(hash_old_object, -1);
+
+	schedule_cache_expiration(cache);
+
+	return 0;
+}
+
+/*!
+ * \internal
+ * \brief Add a new object to the cache.
+ *
+ * \pre cache->objects is write-locked
+ *
+ * \param cache The cache in which to add the new object
+ * \param cached_object The object to add to the cache
+ *
+ * \retval 0 Success
+ * \retval non-zero Failure
+ */
+static int add_to_cache(struct sorcery_memory_cache *cache,
+		struct sorcery_memory_cached_object *cached_object)
+{
+	struct sorcery_memory_cached_object *front;
+
+	if (!ao2_link_flags(cache->objects, cached_object, OBJ_NOLOCK)) {
+		return -1;
+	}
+
+	if (cache->full_backend_cache && (front = ast_heap_peek(cache->object_heap, 1))) {
+		/* For a full backend cache all objects share the same lifetime */
+		cached_object->created = front->created;
+	}
+
+	if (ast_heap_push(cache->object_heap, cached_object)) {
+		ao2_find(cache->objects, cached_object,
+			OBJ_SEARCH_OBJECT | OBJ_UNLINK | OBJ_NODATA | OBJ_NOLOCK);
+		return -1;
+	}
+
+	if (cache->expire_id == -1) {
+		schedule_cache_expiration(cache);
+	}
+
+	return 0;
+}
+
+/*!
+ * \internal
+ * \brief Allocate a cached object for caching an object
+ *
+ * \param sorcery The sorcery instance
+ * \param cache The sorcery memory cache
+ * \param object The object to cache
+ *
+ * \retval non-NULL success
+ * \retval NULL failure
+ */
+static struct sorcery_memory_cached_object *sorcery_memory_cached_object_alloc(const struct ast_sorcery *sorcery,
+	const struct sorcery_memory_cache *cache, void *object)
+{
+	struct sorcery_memory_cached_object *cached;
+
+	cached = ao2_alloc(sizeof(*cached), sorcery_memory_cached_object_destructor);
+	if (!cached) {
+		return NULL;
+	}
+
+	cached->object = ao2_bump(object);
+	cached->created = ast_tvnow();
+	cached->stale_update_sched_id = -1;
+
+	if (cache->full_backend_cache) {
+		/* A cached objectset allows us to easily perform all retrieval operations in a
+		 * minimal of time.
+		 */
+		cached->objectset = ast_sorcery_objectset_create(sorcery, object);
+		if (!cached->objectset) {
+			ao2_ref(cached, -1);
+			return NULL;
+		}
+	}
+
+	return cached;
+}
+
+/*!
+ * \internal
+ * \brief Callback function to cache an object in a memory cache
+ *
+ * \param sorcery The sorcery instance
+ * \param data The sorcery memory cache
+ * \param object The object to cache
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+static int sorcery_memory_cache_create(const struct ast_sorcery *sorcery, void *data, void *object)
+{
+	struct sorcery_memory_cache *cache = data;
+	struct sorcery_memory_cached_object *cached;
+
+	cached = sorcery_memory_cached_object_alloc(sorcery, cache, object);
+	if (!cached) {
+		return -1;
+	}
+
+	/* As there is no guarantee that this won't be called by multiple threads wanting to cache
+	 * the same object we remove any old ones, which turns this into a create/update function
+	 * in reality. As well since there's no guarantee that the object in the cache is the same
+	 * one here we remove any old objects using the object identifier.
+	 */
+
+	ao2_wrlock(cache->objects);
+	remove_from_cache(cache, ast_sorcery_object_get_id(object), 1);
+	if (cache->maximum_objects && ao2_container_count(cache->objects) >= cache->maximum_objects) {
+		if (remove_oldest_from_cache(cache)) {
+			ast_log(LOG_ERROR, "Unable to make room in cache for sorcery object '%s'.\n",
+				ast_sorcery_object_get_id(object));
+			ao2_unlock(cache->objects);
+			ao2_ref(cached, -1);
+			return -1;
+		}
+		ast_assert(ao2_container_count(cache->objects) != cache->maximum_objects);
+	}
+	if (add_to_cache(cache, cached)) {
+		ast_log(LOG_ERROR, "Unable to add object '%s' to the cache\n",
+			ast_sorcery_object_get_id(object));
+		ao2_unlock(cache->objects);
+		ao2_ref(cached, -1);
+		return -1;
+	}
+	ao2_unlock(cache->objects);
+
+	ao2_ref(cached, -1);
+	return 0;
+}
+
+/*!
+ * \internal
+ * \brief AO2 callback function for adding an object to a memory cache
+ *
+ * \param obj The cached object
+ * \param arg The sorcery instance
+ * \param data The cache itself
+ * \param flags Unused flags
+ */
+static int object_add_to_cache_callback(void *obj, void *arg, void *data, int flags)
+{
+	struct sorcery_memory_cache *cache = data;
+	struct sorcery_memory_cached_object *cached;
+
+	cached = sorcery_memory_cached_object_alloc(arg, cache, obj);
+	if (!cached) {
+		return CMP_STOP;
+	}
+
+	add_to_cache(cache, cached);
+	ao2_ref(cached, -1);
+
+	return 0;
+}
+
+struct stale_cache_update_task_data {
+	struct ast_sorcery *sorcery;
+	struct sorcery_memory_cache *cache;
+	char *type;
+};
+
+static void stale_cache_update_task_data_destructor(void *obj)
+{
+	struct stale_cache_update_task_data *task_data = obj;
+
+	ao2_cleanup(task_data->cache);
+	ast_sorcery_unref(task_data->sorcery);
+	ast_free(task_data->type);
+}
+
+static struct stale_cache_update_task_data *stale_cache_update_task_data_alloc(struct ast_sorcery *sorcery,
+		struct sorcery_memory_cache *cache, const char *type)
+{
+	struct stale_cache_update_task_data *task_data;
+
+	task_data = ao2_alloc_options(sizeof(*task_data), stale_cache_update_task_data_destructor,
+		AO2_ALLOC_OPT_LOCK_NOLOCK);
+	if (!task_data) {
+		return NULL;
+	}
+
+	task_data->sorcery = ao2_bump(sorcery);
+	task_data->cache = ao2_bump(cache);
+	task_data->type = ast_strdup(type);
+	if (!task_data->type) {
+		ao2_ref(task_data, -1);
+		return NULL;
+	}
+
+	return task_data;
+}
+
+static int stale_cache_update(const void *data)
+{
+	struct stale_cache_update_task_data *task_data = (struct stale_cache_update_task_data *) data;
+	struct ao2_container *backend_objects;
+
+	start_passthru_update();
+	backend_objects = ast_sorcery_retrieve_by_fields(task_data->sorcery, task_data->type,
+		AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+	end_passthru_update();
+
+	if (!backend_objects) {
+		task_data->cache->stale_update_sched_id = -1;
+		ao2_ref(task_data, -1);
+		return 0;
+	}
+
+	if (task_data->cache->maximum_objects && ao2_container_count(backend_objects) >= task_data->cache->maximum_objects) {
+		ast_log(LOG_ERROR, "The backend contains %d objects while the sorcery memory cache '%s' is explicitly configured to only allow %d\n",
+			ao2_container_count(backend_objects), task_data->cache->name, task_data->cache->maximum_objects);
+		task_data->cache->stale_update_sched_id = -1;
+		ao2_ref(task_data, -1);
+		return 0;
+	}
+
+	ao2_wrlock(task_data->cache->objects);
+	remove_all_from_cache(task_data->cache);
+	ao2_callback_data(backend_objects, OBJ_NOLOCK | OBJ_NODATA | OBJ_MULTIPLE, object_add_to_cache_callback,
+		task_data->sorcery, task_data->cache);
+
+	/* If the number of cached objects does not match the number of backend objects we encountered a memory allocation
+	 * failure and the cache is incomplete, so drop everything and fall back to querying the backend directly
+	 * as it may be able to provide what is wanted.
+	 */
+	if (ao2_container_count(task_data->cache->objects) != ao2_container_count(backend_objects)) {
+		ast_log(LOG_WARNING, "The backend contains %d objects while only %d could be added to sorcery memory cache '%s'\n",
+			ao2_container_count(backend_objects), ao2_container_count(task_data->cache->objects), task_data->cache->name);
+		remove_all_from_cache(task_data->cache);
+	}
+
+	ao2_unlock(task_data->cache->objects);
+	ao2_ref(backend_objects, -1);
+
+	task_data->cache->stale_update_sched_id = -1;
+	ao2_ref(task_data, -1);
+
+	return 0;
+}
+
+struct stale_update_task_data {
+	struct ast_sorcery *sorcery;
+	struct sorcery_memory_cache *cache;
+	void *object;
+};
+
+static void stale_update_task_data_destructor(void *obj)
+{
+	struct stale_update_task_data *task_data = obj;
+
+	ao2_cleanup(task_data->cache);
+	ao2_cleanup(task_data->object);
+	ast_sorcery_unref(task_data->sorcery);
+}
+
+static struct stale_update_task_data *stale_update_task_data_alloc(struct ast_sorcery *sorcery,
+		struct sorcery_memory_cache *cache, const char *type, void *object)
+{
+	struct stale_update_task_data *task_data;
+
+	task_data = ao2_alloc_options(sizeof(*task_data), stale_update_task_data_destructor,
+		AO2_ALLOC_OPT_LOCK_NOLOCK);
+	if (!task_data) {
+		return NULL;
+	}
+
+	task_data->sorcery = ao2_bump(sorcery);
+	task_data->cache = ao2_bump(cache);
+	task_data->object = ao2_bump(object);
+
+	return task_data;
+}
+
+static int stale_item_update(const void *data)
+{
+	struct stale_update_task_data *task_data = (struct stale_update_task_data *) data;
+	void *object;
+
+	start_passthru_update();
+
+	object = ast_sorcery_retrieve_by_id(task_data->sorcery,
+		ast_sorcery_object_get_type(task_data->object),
+		ast_sorcery_object_get_id(task_data->object));
+	if (!object) {
+		ast_debug(1, "Backend no longer has object type '%s' ID '%s'. Removing from cache\n",
+			ast_sorcery_object_get_type(task_data->object),
+			ast_sorcery_object_get_id(task_data->object));
+		sorcery_memory_cache_delete(task_data->sorcery, task_data->cache,
+			task_data->object);
+	} else {
+		ast_debug(1, "Refreshing stale cache object type '%s' ID '%s'\n",
+			ast_sorcery_object_get_type(task_data->object),
+			ast_sorcery_object_get_id(task_data->object));
+		sorcery_memory_cache_create(task_data->sorcery, task_data->cache,
+			object);
+	}
+
+	ast_test_suite_event_notify("SORCERY_MEMORY_CACHE_REFRESHED", "Cache: %s\r\nType: %s\r\nName: %s\r\n",
+		task_data->cache->name, ast_sorcery_object_get_type(task_data->object),
+		ast_sorcery_object_get_id(task_data->object));
+
+	ao2_ref(task_data, -1);
+	end_passthru_update();
+
+	return 0;
+}
+
+/*!
+ * \internal
+ * \brief Populate the cache with all objects from the backend
+ *
+ * \pre cache->objects is write-locked
+ *
+ * \param sorcery The sorcery instance
+ * \param type The type of object
+ * \param cache The sorcery memory cache
+ */
+static void memory_cache_populate(const struct ast_sorcery *sorcery, const char *type, struct sorcery_memory_cache *cache)
+{
+	struct ao2_container *backend_objects;
+
+	start_passthru_update();
+	backend_objects = ast_sorcery_retrieve_by_fields(sorcery, type, AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+	end_passthru_update();
+
+	if (!backend_objects) {
+		/* This will occur in off-nominal memory allocation failure scenarios */
+		return;
+	}
+
+	if (cache->maximum_objects && ao2_container_count(backend_objects) >= cache->maximum_objects) {
+		ast_log(LOG_ERROR, "The backend contains %d objects while the sorcery memory cache '%s' is explicitly configured to only allow %d\n",
+			ao2_container_count(backend_objects), cache->name, cache->maximum_objects);
+		return;
+	}
+
+	ao2_callback_data(backend_objects, OBJ_NOLOCK | OBJ_NODATA | OBJ_MULTIPLE, object_add_to_cache_callback,
+		(struct ast_sorcery*)sorcery, cache);
+
+	/* If the number of cached objects does not match the number of backend objects we encountered a memory allocation
+	 * failure and the cache is incomplete, so drop everything and fall back to querying the backend directly
+	 * as it may be able to provide what is wanted.
+	 */
+	if (ao2_container_count(cache->objects) != ao2_container_count(backend_objects)) {
+		ast_log(LOG_WARNING, "The backend contains %d objects while only %d could be added to sorcery memory cache '%s'\n",
+			ao2_container_count(backend_objects), ao2_container_count(cache->objects), cache->name);
+		remove_all_from_cache(cache);
+	}
+
+	ao2_ref(backend_objects, -1);
+}
+
+/*!
+ * \internal
+ * \brief Determine if a full backend cache update is needed and do it
+ *
+ * \param sorcery The sorcery instance
+ * \param type The type of object
+ * \param cache The sorcery memory cache
+ */
+static void memory_cache_full_update(const struct ast_sorcery *sorcery, const char *type, struct sorcery_memory_cache *cache)
+{
+	if (!cache->full_backend_cache) {
+		return;
+	}
+
+	ao2_wrlock(cache->objects);
+	if (!ao2_container_count(cache->objects)) {
+		memory_cache_populate(sorcery, type, cache);
+	}
+	ao2_unlock(cache->objects);
+}
+
+/*!
+ * \internal
+ * \brief Queue a full cache update
+ *
+ * \param sorcery The sorcery instance
+ * \param cache The sorcery memory cache
+ * \param type The type of object
+ */
+static void memory_cache_stale_update_full(const struct ast_sorcery *sorcery, struct sorcery_memory_cache *cache,
+	const char *type)
+{
+	ao2_wrlock(cache->objects);
+	if (cache->stale_update_sched_id == -1) {
+		struct stale_cache_update_task_data *task_data;
+
+		task_data = stale_cache_update_task_data_alloc((struct ast_sorcery *) sorcery,
+			cache, type);
+		if (task_data) {
+			cache->stale_update_sched_id = ast_sched_add(sched, 1,
+				stale_cache_update, task_data);
+		}
+		if (cache->stale_update_sched_id < 0) {
+			ao2_cleanup(task_data);
+		}
+	}
+	ao2_unlock(cache->objects);
+}
+
+/*!
+ * \internal
+ * \brief Queue a stale object update
+ *
+ * \param sorcery The sorcery instance
+ * \param cache The sorcery memory cache
+ * \param cached The cached object
+ */
+static void memory_cache_stale_update_object(const struct ast_sorcery *sorcery, struct sorcery_memory_cache *cache,
+	struct sorcery_memory_cached_object *cached)
+{
+	ao2_lock(cached);
+	if (cached->stale_update_sched_id == -1) {
+		struct stale_update_task_data *task_data;
+
+		task_data = stale_update_task_data_alloc((struct ast_sorcery *) sorcery,
+			cache, ast_sorcery_object_get_type(cached->object), cached->object);
+		if (task_data) {
+			ast_debug(1, "Cached sorcery object type '%s' ID '%s' is stale. Refreshing\n",
+				ast_sorcery_object_get_type(cached->object), ast_sorcery_object_get_id(cached->object));
+			cached->stale_update_sched_id = ast_sched_add(sched, 1,
+				stale_item_update, task_data);
+		}
+		if (cached->stale_update_sched_id < 0) {
+			ao2_cleanup(task_data);
+			ast_log(LOG_ERROR, "Unable to update stale cached object type '%s', ID '%s'.\n",
+				ast_sorcery_object_get_type(cached->object), ast_sorcery_object_get_id(cached->object));
+		}
+	}
+	ao2_unlock(cached);
+}
+
+/*!
+ * \internal
+ * \brief Check whether an object (or cache) is stale and queue an update
+ *
+ * \param sorcery The sorcery instance
+ * \param cache The sorcery memory cache
+ * \param cached The cached object
+ */
+static void memory_cache_stale_check_object(const struct ast_sorcery *sorcery, struct sorcery_memory_cache *cache,
+	struct sorcery_memory_cached_object *cached)
+{
+	struct timeval elapsed;
+
+	if (!cache->object_lifetime_stale) {
+		return;
+	}
+
+	/* For a full cache as every object has the same expiration/staleness we can do the same check */
+	elapsed = ast_tvsub(ast_tvnow(), cached->created);
+
+	if (elapsed.tv_sec < cache->object_lifetime_stale) {
+		return;
+	}
+
+	if (cache->full_backend_cache) {
+		memory_cache_stale_update_full(sorcery, cache, ast_sorcery_object_get_type(cached->object));
+	} else {
+		memory_cache_stale_update_object(sorcery, cache, cached);
+	}
+
+}
+
+/*!
+ * \internal
+ * \brief Check whether the entire cache is stale or not and queue an update
+ *
+ * \param sorcery The sorcery instance
+ * \param cache The sorcery memory cache
+ *
+ * \note Unlike \ref memory_cache_stale_check this does not require  an explicit object
+ */
+static void memory_cache_stale_check(const struct ast_sorcery *sorcery, struct sorcery_memory_cache *cache)
+{
+	struct sorcery_memory_cached_object *cached;
+
+	ao2_rdlock(cache->objects);
+	cached = ao2_bump(ast_heap_peek(cache->object_heap, 1));
+	ao2_unlock(cache->objects);
+
+	if (!cached) {
+		return;
+	}
+
+	memory_cache_stale_check_object(sorcery, cache, cached);
+	ao2_ref(cached, -1);
+}
+
+/*!
+ * \internal
+ * \brief Callback function to retrieve an object from a memory cache
+ *
+ * \param sorcery The sorcery instance
+ * \param data The sorcery memory cache
+ * \param type The type of the object to retrieve
+ * \param id The id of the object to retrieve
+ *
+ * \retval non-NULL success
+ * \retval NULL failure
+ */
+static void *sorcery_memory_cache_retrieve_id(const struct ast_sorcery *sorcery, void *data, const char *type, const char *id)
+{
+	struct sorcery_memory_cache *cache = data;
+	struct sorcery_memory_cached_object *cached;
+	void *object;
+
+	if (is_passthru_update()) {
+		return NULL;
+	}
+
+	memory_cache_full_update(sorcery, type, cache);
+
+	cached = ao2_find(cache->objects, id, OBJ_SEARCH_KEY);
+	if (!cached) {
+		return NULL;
+	}
+
+	ast_assert(!strcmp(ast_sorcery_object_get_id(cached->object), id));
+
+	memory_cache_stale_check_object(sorcery, cache, cached);
+
+	object = ao2_bump(cached->object);
+	ao2_ref(cached, -1);
+
+	return object;
+}
+
+/*!
+ * \internal
+ * \brief AO2 callback function for comparing a retrieval request and finding applicable objects
+ *
+ * \param obj The cached object
+ * \param arg The comparison parameters
+ * \param flags Unused flags
+ */
+static int sorcery_memory_cache_fields_cmp(void *obj, void *arg, int flags)
+{
+	struct sorcery_memory_cached_object *cached = obj;
+	const struct sorcery_memory_cache_fields_cmp_params *params = arg;
+	RAII_VAR(struct ast_variable *, diff, NULL, ast_variables_destroy);
+
+	if (params->regex) {
+		/* If a regular expression has been provided see if it matches, otherwise move on */
+		if (!regexec(params->regex, ast_sorcery_object_get_id(cached->object), 0, NULL, 0)) {
+			ao2_link(params->container, cached->object);
+		}
+		return 0;
+	} else if (params->fields &&
+	     (ast_sorcery_changeset_create(cached->objectset, params->fields, &diff) ||
+	     diff)) {
+		/* If we can't turn the object into an object set OR if differences exist between the fields
+		 * passed in and what are present on the object they are not a match.
+		 */
+		return 0;
+	}
+
+	if (params->container) {
+		ao2_link(params->container, cached->object);
+
+		/* As multiple objects are being returned keep going */
+		return 0;
+	} else {
+		/* Immediately stop and return, we only want a single object */
+		return CMP_MATCH | CMP_STOP;
+	}
+}
+
+/*!
+ * \internal
+ * \brief Callback function to retrieve a single object based on fields
+ *
+ * \param sorcery The sorcery instance
+ * \param data The sorcery memory cache
+ * \param type The type of the object to retrieve
+ * \param fields Any explicit fields to search for
+ */
+static void *sorcery_memory_cache_retrieve_fields(const struct ast_sorcery *sorcery, void *data, const char *type,
+	const struct ast_variable *fields)
+{
+	struct sorcery_memory_cache *cache = data;
+	struct sorcery_memory_cache_fields_cmp_params params = {
+		.sorcery = sorcery,
+		.cache = cache,
+		.fields = fields,
+	};
+	struct sorcery_memory_cached_object *cached;
+	void *object = NULL;
+
+	if (is_passthru_update() || !cache->full_backend_cache || !fields) {
+		return NULL;
+	}
+
+	cached = ao2_callback(cache->objects, 0, sorcery_memory_cache_fields_cmp, &params);
+
+	if (cached) {
+		memory_cache_stale_check_object(sorcery, cache, cached);
+		object = ao2_bump(cached->object);
+		ao2_ref(cached, -1);
+	}
+
+	return object;
+}
+
+/*!
+ * \internal
+ * \brief Callback function to retrieve multiple objects from a memory cache
+ *
+ * \param sorcery The sorcery instance
+ * \param data The sorcery memory cache
+ * \param type The type of the object to retrieve
+ * \param objects Container to place the objects into
+ * \param fields Any explicit fields to search for
+ */
+static void sorcery_memory_cache_retrieve_multiple(const struct ast_sorcery *sorcery, void *data, const char *type,
+	struct ao2_container *objects, const struct ast_variable *fields)
+{
+	struct sorcery_memory_cache *cache = data;
+	struct sorcery_memory_cache_fields_cmp_params params = {
+		.sorcery = sorcery,
+		.cache = cache,
+		.fields = fields,
+		.container = objects,
+	};
+
+	if (is_passthru_update() || !cache->full_backend_cache) {
+		return;
+	}
+
+	memory_cache_full_update(sorcery, type, cache);
+	ao2_callback(cache->objects, 0, sorcery_memory_cache_fields_cmp, &params);
+
+	if (ao2_container_count(objects)) {
+		memory_cache_stale_check(sorcery, cache);
+	}
+}
+
+/*!
+ * \internal
+ * \brief Callback function to retrieve multiple objects using a regex on the object id
+ *
+ * \param sorcery The sorcery instance
+ * \param data The sorcery memory cache
+ * \param type The type of the object to retrieve
+ * \param objects Container to place the objects into
+ * \param regex Regular expression to apply to the object id
+ */
+static void sorcery_memory_cache_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type,
+	struct ao2_container *objects, const char *regex)
+{
+	struct sorcery_memory_cache *cache = data;
+	regex_t expression;
+	struct sorcery_memory_cache_fields_cmp_params params = {
+		.sorcery = sorcery,
+		.cache = cache,
+		.container = objects,
+		.regex = &expression,
+	};
+
+	if (is_passthru_update() || !cache->full_backend_cache || regcomp(&expression, regex, REG_EXTENDED | REG_NOSUB)) {
+		return;
+	}
+
+	memory_cache_full_update(sorcery, type, cache);
+	ao2_callback(cache->objects, 0, sorcery_memory_cache_fields_cmp, &params);
+	regfree(&expression);
+
+	if (ao2_container_count(objects)) {
+		memory_cache_stale_check(sorcery, cache);
+	}
+}
+
+/*!
+ * \internal
+ * \brief Callback function to finish configuring the memory cache
+ *
+ * \param data The sorcery memory cache
+ * \param sorcery The sorcery instance
+ * \param type The type of object being loaded
+ */
+static void sorcery_memory_cache_load(void *data, const struct ast_sorcery *sorcery, const char *type)
+{
+	struct sorcery_memory_cache *cache = data;
+
+	/* If no name was explicitly specified generate one given the sorcery instance and object type */
+	if (ast_strlen_zero(cache->name)) {
+		ast_asprintf(&cache->name, "%s/%s", ast_sorcery_get_module(sorcery), type);
+	}
+
+	ao2_link(caches, cache);
+	ast_debug(1, "Memory cache '%s' associated with sorcery instance '%p' of module '%s' with object type '%s'\n",
+		cache->name, sorcery, ast_sorcery_get_module(sorcery), type);
+
+	if (cache->full_backend_cache) {
+		cache->sorcery = sorcery;
+		cache->object_type = ast_strdup(type);
+	}
+}
+
+/*!
+ * \internal
+ * \brief Callback function to expire objects from the memory cache on reload (if configured)
+ *
+ * \param data The sorcery memory cache
+ * \param sorcery The sorcery instance
+ * \param type The type of object being reloaded
+ */
+static void sorcery_memory_cache_reload(void *data, const struct ast_sorcery *sorcery, const char *type)
+{
+	struct sorcery_memory_cache *cache = data;
+
+	if (!cache->expire_on_reload) {
+		return;
+	}
+
+	ao2_wrlock(cache->objects);
+	remove_all_from_cache(cache);
+	ao2_unlock(cache->objects);
+}
+
+/*!
+ * \internal
+ * \brief Function used to take an unsigned integer based configuration option and parse it
+ *
+ * \param value The string value of the configuration option
+ * \param result The unsigned integer to place the result in
+ *
+ * \retval 0 failure
+ * \retval 1 success
+ */
+static int configuration_parse_unsigned_integer(const char *value, unsigned int *result)
+{
+	if (ast_strlen_zero(value) || !strncmp(value, "-", 1)) {
+		return 0;
+	}
+
+	return sscanf(value, "%30u", result);
+}
+
+static int age_cmp(void *a, void *b)
+{
+	return ast_tvcmp(((struct sorcery_memory_cached_object *) b)->created,
+			((struct sorcery_memory_cached_object *) a)->created);
+}
+
+/*!
+ * \internal
+ * \brief Callback function to create a new sorcery memory cache using provided configuration
+ *
+ * \param data A stringified configuration for the memory cache
+ *
+ * \retval non-NULL success
+ * \retval NULL failure
+ */
+static void *sorcery_memory_cache_open(const char *data)
+{
+	char *options = ast_strdup(data), *option;
+	RAII_VAR(struct sorcery_memory_cache *, cache, NULL, ao2_cleanup);
+
+	cache = ao2_alloc_options(sizeof(*cache), sorcery_memory_cache_destructor, AO2_ALLOC_OPT_LOCK_NOLOCK);
+	if (!cache) {
+		return NULL;
+	}
+
+	cache->expire_id = -1;
+	cache->stale_update_sched_id = -1;
+
+	/* If no configuration options have been provided this memory cache will operate in a default
+	 * configuration.
+	 */
+	while (!ast_strlen_zero(options) && (option = strsep(&options, ","))) {
+		char *name = strsep(&option, "="), *value = option;
+
+		if (!strcasecmp(name, "name")) {
+			if (ast_strlen_zero(value)) {
+				ast_log(LOG_ERROR, "A name must be specified for the memory cache\n");
+				return NULL;
+			}
+			ast_free(cache->name);
+			cache->name = ast_strdup(value);
+		} else if (!strcasecmp(name, "maximum_objects")) {
+			if (configuration_parse_unsigned_integer(value, &cache->maximum_objects) != 1) {
+				ast_log(LOG_ERROR, "Unsupported maximum objects value of '%s' used for memory cache\n",
+					value);
+				return NULL;
+			}
+		} else if (!strcasecmp(name, "object_lifetime_maximum")) {
+			if (configuration_parse_unsigned_integer(value, &cache->object_lifetime_maximum) != 1) {
+				ast_log(LOG_ERROR, "Unsupported object maximum lifetime value of '%s' used for memory cache\n",
+					value);
+				return NULL;
+			}
+		} else if (!strcasecmp(name, "object_lifetime_stale")) {
+			if (configuration_parse_unsigned_integer(value, &cache->object_lifetime_stale) != 1) {
+				ast_log(LOG_ERROR, "Unsupported object stale lifetime value of '%s' used for memory cache\n",
+					value);
+				return NULL;
+			}
+		} else if (!strcasecmp(name, "expire_on_reload")) {
+			cache->expire_on_reload = ast_true(value);
+		} else if (!strcasecmp(name, "full_backend_cache")) {
+			cache->full_backend_cache = ast_true(value);
+		} else {
+			ast_log(LOG_ERROR, "Unsupported option '%s' used for memory cache\n", name);
+			return NULL;
+		}
+	}
+
+	cache->objects = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_RWLOCK,
+		cache->maximum_objects ? cache->maximum_objects : CACHE_CONTAINER_BUCKET_SIZE,
+		sorcery_memory_cached_object_hash, sorcery_memory_cached_object_cmp);
+	if (!cache->objects) {
+		ast_log(LOG_ERROR, "Could not create a container to hold cached objects for memory cache\n");
+		return NULL;
+	}
+
+	cache->object_heap = ast_heap_create(CACHE_HEAP_INIT_HEIGHT, age_cmp,
+		offsetof(struct sorcery_memory_cached_object, __heap_index));
+	if (!cache->object_heap) {
+		ast_log(LOG_ERROR, "Could not create heap to hold cached objects\n");
+		return NULL;
+	}
+
+	/* The memory cache is not linked to the caches container until the load callback is invoked.
+	 * Linking occurs there so an intelligent cache name can be constructed using the module of
+	 * the sorcery instance and the specific object type if no cache name was specified as part
+	 * of the configuration.
+	 */
+
+	/* This is done as RAII_VAR will drop the reference */
+	return ao2_bump(cache);
+}
+
+/*!
+ * \internal
+ * \brief Callback function to delete an object from a memory cache
+ *
+ * \param sorcery The sorcery instance
+ * \param data The sorcery memory cache
+ * \param object The object to cache
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+static int sorcery_memory_cache_delete(const struct ast_sorcery *sorcery, void *data, void *object)
+{
+	struct sorcery_memory_cache *cache = data;
+	int res;
+
+	ao2_wrlock(cache->objects);
+	res = remove_from_cache(cache, ast_sorcery_object_get_id(object), 1);
+	ao2_unlock(cache->objects);
+
+	if (res) {
+		ast_log(LOG_ERROR, "Unable to delete object '%s' from sorcery cache\n", ast_sorcery_object_get_id(object));
+	}
+
+	return res;
+}
+
+/*!
+ * \internal
+ * \brief Callback function to terminate a memory cache
+ *
+ * \param data The sorcery memory cache
+ */
+static void sorcery_memory_cache_close(void *data)
+{
+	struct sorcery_memory_cache *cache = data;
+
+	/* This can occur if a cache is created but never loaded */
+	if (!ast_strlen_zero(cache->name)) {
+		ao2_unlink(caches, cache);
+	}
+
+	if (cache->object_lifetime_maximum) {
+		/* If object lifetime support is enabled we need to explicitly drop all cached objects here
+		 * and stop the scheduled task. Failure to do so could potentially keep the cache around for
+		 * a prolonged period of time.
+		 */
+		ao2_wrlock(cache->objects);
+		remove_all_from_cache(cache);
+		ao2_unlock(cache->objects);
+	}
+
+	if (cache->full_backend_cache) {
+		ao2_wrlock(cache->objects);
+		cache->sorcery = NULL;
+		ao2_unlock(cache->objects);
+	}
+
+	ao2_ref(cache, -1);
+}
+
+/*!
+ * \internal
+ * \brief CLI tab completion for cache names
+ */
+static char *sorcery_memory_cache_complete_name(const char *word, int state)
+{
+	struct sorcery_memory_cache *cache;
+	struct ao2_iterator it_caches;
+	int wordlen = strlen(word);
+	int which = 0;
+	char *result = NULL;
+
+	it_caches = ao2_iterator_init(caches, 0);
+	while ((cache = ao2_iterator_next(&it_caches))) {
+		if (!strncasecmp(word, cache->name, wordlen)
+			&& ++which > state) {
+			result = ast_strdup(cache->name);
+		}
+		ao2_ref(cache, -1);
+		if (result) {
+			break;
+		}
+	}
+	ao2_iterator_destroy(&it_caches);
+	return result;
+}
+
+/*!
+ * \internal
+ * \brief CLI command implementation for 'sorcery memory cache show'
+ */
+static char *sorcery_memory_cache_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	struct sorcery_memory_cache *cache;
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "sorcery memory cache show";
+		e->usage =
+		    "Usage: sorcery memory cache show <name>\n"
+		    "       Show sorcery memory cache configuration and statistics.\n";
+		return NULL;
+	case CLI_GENERATE:
+		if (a->pos == 4) {
+			return sorcery_memory_cache_complete_name(a->word, a->n);
+		} else {
+			return NULL;
+		}
+	}
+
+	if (a->argc != 5) {
+		return CLI_SHOWUSAGE;
+	}
+
+	cache = ao2_find(caches, a->argv[4], OBJ_SEARCH_KEY);
+	if (!cache) {
+		ast_cli(a->fd, "Specified sorcery memory cache '%s' does not exist\n", a->argv[4]);
+		return CLI_FAILURE;
+	}
+
+	ast_cli(a->fd, "Sorcery memory cache: %s\n", cache->name);
+	ast_cli(a->fd, "Number of objects within cache: %d\n", ao2_container_count(cache->objects));
+	if (cache->maximum_objects) {
+		ast_cli(a->fd, "Maximum allowed objects: %d\n", cache->maximum_objects);
+	} else {
+		ast_cli(a->fd, "There is no limit on the maximum number of objects in the cache\n");
+	}
+	if (cache->object_lifetime_maximum) {
+		ast_cli(a->fd, "Number of seconds before object expires: %d\n", cache->object_lifetime_maximum);
+	} else {
+		ast_cli(a->fd, "Object expiration is not enabled - cached objects will not expire\n");
+	}
+	if (cache->object_lifetime_stale) {
+		ast_cli(a->fd, "Number of seconds before object becomes stale: %d\n", cache->object_lifetime_stale);
+	} else {
+		ast_cli(a->fd, "Object staleness is not enabled - cached objects will not go stale\n");
+	}
+	ast_cli(a->fd, "Expire all objects on reload: %s\n", AST_CLI_ONOFF(cache->expire_on_reload));
+
+	ao2_ref(cache, -1);
+
+	return CLI_SUCCESS;
+}
+
+/*! \brief Structure used to pass data for printing cached object information */
+struct print_object_details {
+	/*! \brief The sorcery memory cache */
+	struct sorcery_memory_cache *cache;
+	/*! \brief The CLI arguments */
+	struct ast_cli_args *a;
+};
+
+/*!
+ * \internal
+ * \brief Callback function for displaying object within the cache
+ */
+static int sorcery_memory_cache_print_object(void *obj, void *arg, int flags)
+{
+#define FORMAT "%-25.25s %-15u %-15u \n"
+	struct sorcery_memory_cached_object *cached = obj;
+	struct print_object_details *details = arg;
+	int seconds_until_expire = 0, seconds_until_stale = 0;
+
+	if (details->cache->object_lifetime_maximum) {
+		seconds_until_expire = ast_tvdiff_ms(ast_tvadd(cached->created, ast_samp2tv(details->cache->object_lifetime_maximum, 1)), ast_tvnow()) / 1000;
+	}
+	if (details->cache->object_lifetime_stale) {
+		seconds_until_stale = ast_tvdiff_ms(ast_tvadd(cached->created, ast_samp2tv(details->cache->object_lifetime_stale, 1)), ast_tvnow()) / 1000;
+	}
+
+	ast_cli(details->a->fd, FORMAT, ast_sorcery_object_get_id(cached->object), MAX(seconds_until_stale, 0), MAX(seconds_until_expire, 0));
+
+	return CMP_MATCH;
+#undef FORMAT
+}
+
+/*!
+ * \internal
+ * \brief CLI command implementation for 'sorcery memory cache dump'
+ */
+static char *sorcery_memory_cache_dump(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define FORMAT "%-25.25s %-15.15s %-15.15s \n"
+	struct sorcery_memory_cache *cache;
+	struct print_object_details details;
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "sorcery memory cache dump";
+		e->usage =
+		    "Usage: sorcery memory cache dump <name>\n"
+		    "       Dump a list of the objects within the cache, listed by object identifier.\n";
+		return NULL;
+	case CLI_GENERATE:
+		if (a->pos == 4) {
+			return sorcery_memory_cache_complete_name(a->word, a->n);
+		} else {
+			return NULL;
+		}
+	}
+
+	if (a->argc != 5) {
+		return CLI_SHOWUSAGE;
+	}
+
+	cache = ao2_find(caches, a->argv[4], OBJ_SEARCH_KEY);
+	if (!cache) {
+		ast_cli(a->fd, "Specified sorcery memory cache '%s' does not exist\n", a->argv[4]);
+		return CLI_FAILURE;
+	}
+
+	details.cache = cache;
+	details.a = a;
+
+	ast_cli(a->fd, "Dumping sorcery memory cache '%s':\n", cache->name);
+	if (!cache->object_lifetime_stale) {
+		ast_cli(a->fd, " * Staleness is not enabled - objects will not go stale\n");
+	}
+	if (!cache->object_lifetime_maximum) {
+		ast_cli(a->fd, " * Object lifetime is not enabled - objects will not expire\n");
+	}
+	ast_cli(a->fd, FORMAT, "Object Name", "Stale In", "Expires In");
+	ast_cli(a->fd, FORMAT, "-------------------------", "---------------", "---------------");
+	ao2_callback(cache->objects, OBJ_NODATA | OBJ_MULTIPLE, sorcery_memory_cache_print_object, &details);
+	ast_cli(a->fd, FORMAT, "-------------------------", "---------------", "---------------");
+	ast_cli(a->fd, "Total number of objects cached: %d\n", ao2_container_count(cache->objects));
+
+	ao2_ref(cache, -1);
+
+	return CLI_SUCCESS;
+#undef FORMAT
+}
+
+/*!
+ * \internal
+ * \brief CLI tab completion for cached object names
+ */
+static char *sorcery_memory_cache_complete_object_name(const char *cache_name, const char *word, int state)
+{
+	struct sorcery_memory_cache *cache;
+	struct sorcery_memory_cached_object *cached;
+	struct ao2_iterator it_cached;
+	int wordlen = strlen(word);
+	int which = 0;
+	char *result = NULL;
+
+	cache = ao2_find(caches, cache_name, OBJ_SEARCH_KEY);
+	if (!cache) {
+		return NULL;
+	}
+
+	it_cached = ao2_iterator_init(cache->objects, 0);
+	while ((cached = ao2_iterator_next(&it_cached))) {
+		if (!strncasecmp(word, ast_sorcery_object_get_id(cached->object), wordlen)
+			&& ++which > state) {
+			result = ast_strdup(ast_sorcery_object_get_id(cached->object));
+		}
+		ao2_ref(cached, -1);
+		if (result) {
+			break;
+		}
+	}
+	ao2_iterator_destroy(&it_cached);
+
+	ao2_ref(cache, -1);
+
+	return result;
+}
+
+/*!
+ * \internal
+ * \brief CLI command implementation for 'sorcery memory cache expire'
+ */
+static char *sorcery_memory_cache_expire(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	struct sorcery_memory_cache *cache;
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "sorcery memory cache expire";
+		e->usage =
+		    "Usage: sorcery memory cache expire <cache name> [object name]\n"
+		    "       Expire a specific object or ALL objects within a sorcery memory cache.\n";
+		return NULL;
+	case CLI_GENERATE:
+		if (a->pos == 4) {
+			return sorcery_memory_cache_complete_name(a->word, a->n);
+		} else if (a->pos == 5) {
+			return sorcery_memory_cache_complete_object_name(a->argv[4], a->word, a->n);
+		} else {
+			return NULL;
+		}
+	}
+
+	if (a->argc > 6) {
+		return CLI_SHOWUSAGE;
+	}
+
+	cache = ao2_find(caches, a->argv[4], OBJ_SEARCH_KEY);
+	if (!cache) {
+		ast_cli(a->fd, "Specified sorcery memory cache '%s' does not exist\n", a->argv[4]);
+		return CLI_FAILURE;
+	}
+
+	ao2_wrlock(cache->objects);
+	if (a->argc == 5) {
+		remove_all_from_cache(cache);
+		ast_cli(a->fd, "All objects have been removed from cache '%s'\n", a->argv[4]);
+	} else {
+		if (!remove_from_cache(cache, a->argv[5], 1)) {
+			ast_cli(a->fd, "Successfully expired object '%s' from cache '%s'\n", a->argv[5], a->argv[4]);
+		} else {
+			ast_cli(a->fd, "Object '%s' was not expired from cache '%s' as it was not found\n", a->argv[5],
+				a->argv[4]);
+		}
+	}
+	ao2_unlock(cache->objects);
+
+	ao2_ref(cache, -1);
+
+	return CLI_SUCCESS;
+}
+
+/*!
+ * \internal
+ * \brief CLI command implementation for 'sorcery memory cache stale'
+ */
+static char *sorcery_memory_cache_stale(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	struct sorcery_memory_cache *cache;
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "sorcery memory cache stale";
+		e->usage =
+		    "Usage: sorcery memory cache stale <cache name> [object name]\n"
+		    "       Mark a specific object or ALL objects as stale in a sorcery memory cache.\n";
+		return NULL;
+	case CLI_GENERATE:
+		if (a->pos == 4) {
+			return sorcery_memory_cache_complete_name(a->word, a->n);
+		} else if (a->pos == 5) {
+			return sorcery_memory_cache_complete_object_name(a->argv[4], a->word, a->n);
+		} else {
+			return NULL;
+		}
+	}
+
+	if (a->argc > 6) {
+		return CLI_SHOWUSAGE;
+	}
+
+	cache = ao2_find(caches, a->argv[4], OBJ_SEARCH_KEY);
+	if (!cache) {
+		ast_cli(a->fd, "Specified sorcery memory cache '%s' does not exist\n", a->argv[4]);
+		return CLI_FAILURE;
+	}
+
+	if (!cache->object_lifetime_stale) {
+		ast_cli(a->fd, "Specified sorcery memory cache '%s' does not have staleness enabled\n", a->argv[4]);
+		ao2_ref(cache, -1);
+		return CLI_FAILURE;
+	}
+
+	ao2_rdlock(cache->objects);
+	if (a->argc == 5) {
+		mark_all_as_stale_in_cache(cache);
+		ast_cli(a->fd, "Marked all objects in sorcery memory cache '%s' as stale\n", a->argv[4]);
+	} else {
+		if (!mark_object_as_stale_in_cache(cache, a->argv[5])) {
+			ast_cli(a->fd, "Successfully marked object '%s' in memory cache '%s' as stale\n",
+				a->argv[5], a->argv[4]);
+		} else {
+			ast_cli(a->fd, "Object '%s' in sorcery memory cache '%s' could not be marked as stale as it was not found\n",
+				a->argv[5], a->argv[4]);
+		}
+	}
+	ao2_unlock(cache->objects);
+
+	ao2_ref(cache, -1);
+
+	return CLI_SUCCESS;
+}
+
+/*!
+ * \internal
+ * \brief CLI command implementation for 'sorcery memory cache populate'
+ */
+static char *sorcery_memory_cache_populate(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	struct sorcery_memory_cache *cache;
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "sorcery memory cache populate";
+		e->usage =
+		    "Usage: sorcery memory cache populate <cache name>\n"
+		    "       Expire all objects in the cache and populate it with ALL objects from backend.\n";
+		return NULL;
+	case CLI_GENERATE:
+		if (a->pos == 4) {
+			return sorcery_memory_cache_complete_name(a->word, a->n);
+		} else {
+			return NULL;
+		}
+	}
+
+	if (a->argc > 5) {
+		return CLI_SHOWUSAGE;
+	}
+
+	cache = ao2_find(caches, a->argv[4], OBJ_SEARCH_KEY);
+	if (!cache) {
+		ast_cli(a->fd, "Specified sorcery memory cache '%s' does not exist\n", a->argv[4]);
+		return CLI_FAILURE;
+	}
+
+	if (!cache->full_backend_cache) {
+		ast_cli(a->fd, "Specified sorcery memory cache '%s' does not have full backend caching enabled\n", a->argv[4]);
+		ao2_ref(cache, -1);
+		return CLI_FAILURE;
+	}
+
+	ao2_wrlock(cache->objects);
+	if (!cache->sorcery) {
+		ast_cli(a->fd, "Specified sorcery memory cache '%s' is no longer active\n", a->argv[4]);
+		ao2_unlock(cache->objects);
+		ao2_ref(cache, -1);
+		return CLI_FAILURE;
+	}
+
+	remove_all_from_cache(cache);
+	memory_cache_populate(cache->sorcery, cache->object_type, cache);
+
+	ast_cli(a->fd, "Specified sorcery memory cache '%s' has been populated with '%d' objects from the backend\n",
+		a->argv[4], ao2_container_count(cache->objects));
+
+	ao2_unlock(cache->objects);
+
+	ao2_ref(cache, -1);
+
+	return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_memory_cache[] = {
+	AST_CLI_DEFINE(sorcery_memory_cache_show, "Show sorcery memory cache information"),
+	AST_CLI_DEFINE(sorcery_memory_cache_dump, "Dump all objects within a sorcery memory cache"),
+	AST_CLI_DEFINE(sorcery_memory_cache_expire, "Expire a specific object or ALL objects within a sorcery memory cache"),
+	AST_CLI_DEFINE(sorcery_memory_cache_stale, "Mark a specific object or ALL objects as stale within a sorcery memory cache"),
+	AST_CLI_DEFINE(sorcery_memory_cache_populate, "Clear and populate the sorcery memory cache with objects from the backend"),
+};
+
+/*!
+ * \internal
+ * \brief AMI command implementation for 'SorceryMemoryCacheExpireObject'
+ */
+static int sorcery_memory_cache_ami_expire_object(struct mansession *s, const struct message *m)
+{
+	const char *cache_name = astman_get_header(m, "Cache");
+	const char *object_name = astman_get_header(m, "Object");
+	struct sorcery_memory_cache *cache;
+	int res;
+
+	if (ast_strlen_zero(cache_name)) {
+		astman_send_error(s, m, "SorceryMemoryCacheExpireObject requires that a cache name be provided.\n");
+		return 0;
+	} else if (ast_strlen_zero(object_name)) {
+		astman_send_error(s, m, "SorceryMemoryCacheExpireObject requires that an object name be provided\n");
+		return 0;
+	}
+
+	cache = ao2_find(caches, cache_name, OBJ_SEARCH_KEY);
+	if (!cache) {
+		astman_send_error(s, m, "The provided cache does not exist\n");
+		return 0;
+	}
+
+	ao2_wrlock(cache->objects);
+	res = remove_from_cache(cache, object_name, 1);
+	ao2_unlock(cache->objects);
+
+	ao2_ref(cache, -1);
+
+	if (!res) {
+		astman_send_ack(s, m, "The provided object was expired from the cache\n");
+	} else {
+		astman_send_error(s, m, "The provided object could not be expired from the cache\n");
+	}
+
+	return 0;
+}
+
+/*!
+ * \internal
+ * \brief AMI command implementation for 'SorceryMemoryCacheExpire'
+ */
+static int sorcery_memory_cache_ami_expire(struct mansession *s, const struct message *m)
+{
+	const char *cache_name = astman_get_header(m, "Cache");
+	struct sorcery_memory_cache *cache;
+
+	if (ast_strlen_zero(cache_name)) {
+		astman_send_error(s, m, "SorceryMemoryCacheExpire requires that a cache name be provided.\n");
+		return 0;
+	}
+
+	cache = ao2_find(caches, cache_name, OBJ_SEARCH_KEY);
+	if (!cache) {
+		astman_send_error(s, m, "The provided cache does not exist\n");
+		return 0;
+	}
+
+	ao2_wrlock(cache->objects);
+	remove_all_from_cache(cache);
+	ao2_unlock(cache->objects);
+
+	ao2_ref(cache, -1);
+
+	astman_send_ack(s, m, "All objects were expired from the cache\n");
+
+	return 0;
+}
+
+/*!
+ * \internal
+ * \brief AMI command implementation for 'SorceryMemoryCacheStaleObject'
+ */
+static int sorcery_memory_cache_ami_stale_object(struct mansession *s, const struct message *m)
+{
+	const char *cache_name = astman_get_header(m, "Cache");
+	const char *object_name = astman_get_header(m, "Object");
+	struct sorcery_memory_cache *cache;
+	int res;
+
+	if (ast_strlen_zero(cache_name)) {
+		astman_send_error(s, m, "SorceryMemoryCacheStaleObject requires that a cache name be provided.\n");
+		return 0;
+	} else if (ast_strlen_zero(object_name)) {
+		astman_send_error(s, m, "SorceryMemoryCacheStaleObject requires that an object name be provided\n");
+		return 0;
+	}
+
+	cache = ao2_find(caches, cache_name, OBJ_SEARCH_KEY);
+	if (!cache) {
+		astman_send_error(s, m, "The provided cache does not exist\n");
+		return 0;
+	}
+
+	ao2_rdlock(cache->objects);
+	res = mark_object_as_stale_in_cache(cache, object_name);
+	ao2_unlock(cache->objects);
+
+	ao2_ref(cache, -1);
+
+	if (!res) {
+		astman_send_ack(s, m, "The provided object was marked as stale in the cache\n");
+	} else {
+		astman_send_error(s, m, "The provided object could not be marked as stale in the cache\n");
+	}
+
+	return 0;
+}
+
+/*!
+ * \internal
+ * \brief AMI command implementation for 'SorceryMemoryCacheStale'
+ */
+static int sorcery_memory_cache_ami_stale(struct mansession *s, const struct message *m)
+{
+	const char *cache_name = astman_get_header(m, "Cache");
+	struct sorcery_memory_cache *cache;
+
+	if (ast_strlen_zero(cache_name)) {
+		astman_send_error(s, m, "SorceryMemoryCacheStale requires that a cache name be provided.\n");
+		return 0;
+	}
+
+	cache = ao2_find(caches, cache_name, OBJ_SEARCH_KEY);
+	if (!cache) {
+		astman_send_error(s, m, "The provided cache does not exist\n");
+		return 0;
+	}
+
+	ao2_rdlock(cache->objects);
+	mark_all_as_stale_in_cache(cache);
+	ao2_unlock(cache->objects);
+
+	ao2_ref(cache, -1);
+
+	astman_send_ack(s, m, "All objects were marked as stale in the cache\n");
+
+	return 0;
+}
+
+/*!
+ * \internal
+ * \brief AMI command implementation for 'SorceryMemoryCachePopulate'
+ */
+static int sorcery_memory_cache_ami_populate(struct mansession *s, const struct message *m)
+{
+	const char *cache_name = astman_get_header(m, "Cache");
+	struct sorcery_memory_cache *cache;
+
+	if (ast_strlen_zero(cache_name)) {
+		astman_send_error(s, m, "SorceryMemoryCachePopulate requires that a cache name be provided.\n");
+		return 0;
+	}
+
+	cache = ao2_find(caches, cache_name, OBJ_SEARCH_KEY);
+	if (!cache) {
+		astman_send_error(s, m, "The provided cache does not exist\n");
+		return 0;
+	}
+
+	if (!cache->full_backend_cache) {
+		astman_send_error(s, m, "The provided cache does not have full backend caching enabled\n");
+		ao2_ref(cache, -1);
+		return 0;
+	}
+
+	ao2_wrlock(cache->objects);
+	if (!cache->sorcery) {
+		astman_send_error(s, m, "The provided cache is no longer active\n");
+		ao2_unlock(cache->objects);
+		ao2_ref(cache, -1);
+		return 0;
+	}
+
+	remove_all_from_cache(cache);
+	memory_cache_populate(cache->sorcery, cache->object_type, cache);
+
+	ao2_unlock(cache->objects);
+
+	ao2_ref(cache, -1);
+
+	astman_send_ack(s, m, "Cache has been expired and populated\n");
+
+	return 0;
+}
+
+#ifdef TEST_FRAMEWORK
+
+/*! \brief Dummy sorcery object */
+struct test_sorcery_object {
+	SORCERY_OBJECT(details);
+};
+
+/*!
+ * \internal
+ * \brief Allocator for test object
+ *
+ * \param id The identifier for the object
+ *
+ * \retval non-NULL success
+ * \retval NULL failure
+ */
+static void *test_sorcery_object_alloc(const char *id)
+{
+	return ast_sorcery_generic_alloc(sizeof(struct test_sorcery_object), NULL);
+}
+
+/*!
+ * \internal
+ * \brief Allocator for test sorcery instance
+ *
+ * \retval non-NULL success
+ * \retval NULL failure
+ */
+static struct ast_sorcery *alloc_and_initialize_sorcery(void)
+{
+	struct ast_sorcery *sorcery;
+
+	if (!(sorcery = ast_sorcery_open())) {
+		return NULL;
+	}
+
+	if ((ast_sorcery_apply_default(sorcery, "test", "memory", NULL) != AST_SORCERY_APPLY_SUCCESS) ||
+		ast_sorcery_internal_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
+		ast_sorcery_unref(sorcery);
+		return NULL;
+	}
+
+	return sorcery;
+}
+
+AST_TEST_DEFINE(open_with_valid_options)
+{
+	int res = AST_TEST_PASS;
+	struct sorcery_memory_cache *cache;
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "open_with_valid_options";
+		info->category = "/res/res_sorcery_memory_cache/";
+		info->summary = "Attempt to create sorcery memory caches using valid options";
+		info->description = "This test performs the following:\n"
+			"\t* Creates a memory cache with default configuration\n"
+			"\t* Creates a memory cache with a maximum object count of 10 and verifies it\n"
+			"\t* Creates a memory cache with a maximum object lifetime of 60 and verifies it\n"
+			"\t* Creates a memory cache with a stale object lifetime of 90 and verifies it";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	cache = sorcery_memory_cache_open("");
+	if (!cache) {
+		ast_test_status_update(test, "Failed to create a sorcery memory cache using default configuration\n");
+		res = AST_TEST_FAIL;
+	} else {
+		sorcery_memory_cache_close(cache);
+	}
+
+	cache = sorcery_memory_cache_open("maximum_objects=10");
+	if (!cache) {
+		ast_test_status_update(test, "Failed to create a sorcery memory cache with a maximum object count of 10\n");
+		res = AST_TEST_FAIL;
+	} else {
+		if (cache->maximum_objects != 10) {
+			ast_test_status_update(test, "Created a sorcery memory cache with a maximum object count of 10 but it has '%u'\n",
+				cache->maximum_objects);
+		}
+		sorcery_memory_cache_close(cache);
+	}
+
+	cache = sorcery_memory_cache_open("object_lifetime_maximum=60");
+	if (!cache) {
+		ast_test_status_update(test, "Failed to create a sorcery memory cache with a maximum object lifetime of 60\n");
+		res = AST_TEST_FAIL;
+	} else {
+		if (cache->object_lifetime_maximum != 60) {
+			ast_test_status_update(test, "Created a sorcery memory cache with a maximum object lifetime of 60 but it has '%u'\n",
+				cache->object_lifetime_maximum);
+		}
+		sorcery_memory_cache_close(cache);
+	}
+
+	cache = sorcery_memory_cache_open("object_lifetime_stale=90");
+	if (!cache) {
+		ast_test_status_update(test, "Failed to create a sorcery memory cache with a stale object lifetime of 90\n");
+		res = AST_TEST_FAIL;
+	} else {
+		if (cache->object_lifetime_stale != 90) {
+			ast_test_status_update(test, "Created a sorcery memory cache with a stale object lifetime of 90 but it has '%u'\n",
+				cache->object_lifetime_stale);
+		}
+		sorcery_memory_cache_close(cache);
+	}
+
+
+	return res;
+}
+
+AST_TEST_DEFINE(open_with_invalid_options)
+{
+	int res = AST_TEST_PASS;
+	struct sorcery_memory_cache *cache;
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "open_with_invalid_options";
+		info->category = "/res/res_sorcery_memory_cache/";
+		info->summary = "Attempt to create sorcery memory caches using invalid options";
+		info->description = "This test attempts to perform the following:\n"
+			"\t* Create a memory cache with an empty name\n"
+			"\t* Create a memory cache with a maximum object count of -1\n"
+			"\t* Create a memory cache with a maximum object count of toast\n"
+			"\t* Create a memory cache with a maximum object lifetime of -1\n"
+			"\t* Create a memory cache with a maximum object lifetime of toast\n"
+			"\t* Create a memory cache with a stale object lifetime of -1\n"
+			"\t* Create a memory cache with a stale object lifetime of toast";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	cache = sorcery_memory_cache_open("name=");
+	if (cache) {
+		ast_test_status_update(test, "Created a sorcery memory cache with an empty name\n");
+		sorcery_memory_cache_close(cache);
+		res = AST_TEST_FAIL;
+	}
+
+	cache = sorcery_memory_cache_open("maximum_objects=-1");
+	if (cache) {
+		ast_test_status_update(test, "Created a sorcery memory cache with a maximum object count of -1\n");
+		sorcery_memory_cache_close(cache);
+		res = AST_TEST_FAIL;
+	}
+
+	cache = sorcery_memory_cache_open("maximum_objects=toast");
+	if (cache) {
+		ast_test_status_update(test, "Created a sorcery memory cache with a maximum object count of toast\n");
+		sorcery_memory_cache_close(cache);
+		res = AST_TEST_FAIL;
+	}
+
+	cache = sorcery_memory_cache_open("object_lifetime_maximum=-1");
+	if (cache) {
+		ast_test_status_update(test, "Created a sorcery memory cache with an object lifetime maximum of -1\n");
+		sorcery_memory_cache_close(cache);
+		res = AST_TEST_FAIL;
+	}
+
+	cache = sorcery_memory_cache_open("object_lifetime_maximum=toast");
+	if (cache) {
+		ast_test_status_update(test, "Created a sorcery memory cache with an object lifetime maximum of toast\n");
+		sorcery_memory_cache_close(cache);
+		res = AST_TEST_FAIL;
+	}
+
+	cache = sorcery_memory_cache_open("object_lifetime_stale=-1");
+	if (cache) {
+		ast_test_status_update(test, "Created a sorcery memory cache with a stale object lifetime of -1\n");
+		sorcery_memory_cache_close(cache);
+		res = AST_TEST_FAIL;
+	}
+
+	cache = sorcery_memory_cache_open("object_lifetime_stale=toast");
+	if (cache) {
+		ast_test_status_update(test, "Created a sorcery memory cache with a stale object lifetime of toast\n");
+		sorcery_memory_cache_close(cache);
+		res = AST_TEST_FAIL;
+	}
+
+	cache = sorcery_memory_cache_open("tacos");
+	if (cache) {
+		ast_test_status_update(test, "Created a sorcery memory cache with an invalid configuration option 'tacos'\n");
+		sorcery_memory_cache_close(cache);
+		res = AST_TEST_FAIL;
+	}
+
+	return res;
+}
+
+AST_TEST_DEFINE(create_and_retrieve)
+{
+	int res = AST_TEST_FAIL;
+	struct ast_sorcery *sorcery = NULL;
+	struct sorcery_memory_cache *cache = NULL;
+	RAII_VAR(void *, object, NULL, ao2_cleanup);
+	RAII_VAR(void *, cached_object, NULL, ao2_cleanup);
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "create";
+		info->category = "/res/res_sorcery_memory_cache/";
+		info->summary = "Attempt to create an object in the cache";
+		info->description = "This test performs the following:\n"
+			"\t* Creates a memory cache with default options\n"
+			"\t* Creates a sorcery instance with a test object\n"
+			"\t* Creates a test object with an id of test\n"
+			"\t* Pushes the test object into the memory cache\n"
+			"\t* Confirms that the test object is in the cache";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	cache = sorcery_memory_cache_open("");
+	if (!cache) {
+		ast_test_status_update(test, "Failed to create a sorcery memory cache using default options\n");
+		goto cleanup;
+	}
+
+	if (ao2_container_count(cache->objects)) {
+		ast_test_status_update(test, "Memory cache contains cached objects before we added one\n");
+		goto cleanup;
+	}
+
+	sorcery = alloc_and_initialize_sorcery();
+	if (!sorcery) {
+		ast_test_status_update(test, "Failed to create a test sorcery instance\n");
+		goto cleanup;
+	}
+
+	object = ast_sorcery_alloc(sorcery, "test", "test");
+	if (!object) {
+		ast_test_status_update(test, "Failed to allocate a test object\n");
+		goto cleanup;
+	}
+
+	sorcery_memory_cache_create(sorcery, cache, object);
+
+	if (!ao2_container_count(cache->objects)) {
+		ast_test_status_update(test, "Added test object to memory cache but cache remains empty\n");
+		goto cleanup;
+	}
+
+	cached_object = sorcery_memory_cache_retrieve_id(sorcery, cache, "test", "test");
+	if (!cached_object) {
+		ast_test_status_update(test, "Object placed into memory cache could not be retrieved\n");
+		goto cleanup;
+	}
+
+	if (cached_object != object) {
+		ast_test_status_update(test, "Object retrieved from memory cached is not the one we cached\n");
+		goto cleanup;
+	}
+
+	res = AST_TEST_PASS;
+
+cleanup:
+	if (cache) {
+		sorcery_memory_cache_close(cache);
+	}
+	if (sorcery) {
+		ast_sorcery_unref(sorcery);
+	}
+
+	return res;
+}
+
+AST_TEST_DEFINE(update)
+{
+	int res = AST_TEST_FAIL;
+	struct ast_sorcery *sorcery = NULL;
+	struct sorcery_memory_cache *cache = NULL;
+	RAII_VAR(void *, original_object, NULL, ao2_cleanup);
+	RAII_VAR(void *, updated_object, NULL, ao2_cleanup);
+	RAII_VAR(void *, cached_object, NULL, ao2_cleanup);
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "create";
+		info->category = "/res/res_sorcery_memory_cache/";
+		info->summary = "Attempt to create and then update an object in the cache";
+		info->description = "This test performs the following:\n"
+			"\t* Creates a memory cache with default options\n"
+			"\t* Creates a sorcery instance with a test object\n"
+			"\t* Creates a test object with an id of test\n"
+			"\t* Pushes the test object into the memory cache\n"
+			"\t* Confirms that the test object is in the cache\n"
+			"\t* Creates a new test object with the same id of test\n"
+			"\t* Pushes the new test object into the memory cache\n"
+			"\t* Confirms that the new test object has replaced the old one";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	cache = sorcery_memory_cache_open("");
+	if (!cache) {
+		ast_test_status_update(test, "Failed to create a sorcery memory cache using default options\n");
+		goto cleanup;
+	}
+
+	if (ao2_container_count(cache->objects)) {
+		ast_test_status_update(test, "Memory cache contains cached objects before we added one\n");
+		goto cleanup;
+	}
+
+	sorcery = alloc_and_initialize_sorcery();
+	if (!sorcery) {
+		ast_test_status_update(test, "Failed to create a test sorcery instance\n");
+		goto cleanup;
+	}
+
+	original_object = ast_sorcery_alloc(sorcery, "test", "test");
+	if (!original_object) {
+		ast_test_status_update(test, "Failed to allocate a test object\n");
+		goto cleanup;
+	}
+
+	sorcery_memory_cache_create(sorcery, cache, original_object);
+
+	updated_object = ast_sorcery_alloc(sorcery, "test", "test");
+	if (!updated_object) {
+		ast_test_status_update(test, "Failed to allocate an updated test object\n");
+		goto cleanup;
+	}
+
+	sorcery_memory_cache_create(sorcery, cache, updated_object);
+
+	if (ao2_container_count(cache->objects) != 1) {
+		ast_test_status_update(test, "Added updated test object to memory cache but cache now contains %d objects instead of 1\n",
+			ao2_container_count(cache->objects));
+		goto cleanup;
+	}
+
+	cached_object = sorcery_memory_cache_retrieve_id(sorcery, cache, "test", "test");
+	if (!cached_object) {
+		ast_test_status_update(test, "Updated object placed into memory cache could not be retrieved\n");
+		goto cleanup;
+	}
+
+	if (cached_object == original_object) {
+		ast_test_status_update(test, "Updated object placed into memory cache but old one is being retrieved\n");
+		goto cleanup;
+	} else if (cached_object != updated_object) {
+		ast_test_status_update(test, "Updated object placed into memory cache but different one is being retrieved\n");
+		goto cleanup;
+	}
+
+	res = AST_TEST_PASS;
+
+cleanup:
+	if (cache) {
+		sorcery_memory_cache_close(cache);
+	}
+	if (sorcery) {
+		ast_sorcery_unref(sorcery);
+	}
+
+	return res;
+}
+
+AST_TEST_DEFINE(delete)
+{
+	int res = AST_TEST_FAIL;
+	struct ast_sorcery *sorcery = NULL;
+	struct sorcery_memory_cache *cache = NULL;
+	RAII_VAR(void *, object, NULL, ao2_cleanup);
+	RAII_VAR(void *, cached_object, NULL, ao2_cleanup);
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "delete";
+		info->category = "/res/res_sorcery_memory_cache/";
+		info->summary = "Attempt to create and then delete an object in the cache";
+		info->description = "This test performs the following:\n"
+			"\t* Creates a memory cache with default options\n"
+			"\t* Creates a sorcery instance with a test object\n"
+			"\t* Creates a test object with an id of test\n"
+			"\t* Pushes the test object into the memory cache\n"
+			"\t* Confirms that the test object is in the cache\n"
+			"\t* Deletes the test object from the cache\n"
+			"\t* Confirms that the test object is no longer in the cache";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	cache = sorcery_memory_cache_open("");
+	if (!cache) {
+		ast_test_status_update(test, "Failed to create a sorcery memory cache using default options\n");
+		goto cleanup;
+	}
+
+	if (ao2_container_count(cache->objects)) {
+		ast_test_status_update(test, "Memory cache contains cached objects before we added one\n");
+		goto cleanup;
+	}
+
+	sorcery = alloc_and_initialize_sorcery();
+	if (!sorcery) {
+		ast_test_status_update(test, "Failed to create a test sorcery instance\n");
+		goto cleanup;
+	}
+
+	object = ast_sorcery_alloc(sorcery, "test", "test");
+	if (!object) {
+		ast_test_status_update(test, "Failed to allocate a test object\n");
+		goto cleanup;
+	}
+
+	sorcery_memory_cache_create(sorcery, cache, object);
+
+	if (!ao2_container_count(cache->objects)) {
+		ast_test_status_update(test, "Added test object to memory cache but cache contains no objects\n");
+		goto cleanup;
+	}
+
+	cached_object = sorcery_memory_cache_retrieve_id(sorcery, cache, "test", "test");
+	if (!cached_object) {
+		ast_test_status_update(test, "Test object placed into memory cache could not be retrieved\n");
+		goto cleanup;
+	}
+
+	ao2_ref(cached_object, -1);
+	cached_object = NULL;
+
+	sorcery_memory_cache_delete(sorcery, cache, object);
+
+	cached_object = sorcery_memory_cache_retrieve_id(sorcery, cache, "test", "test");
+	if (cached_object) {
+		ast_test_status_update(test, "Test object deleted from memory cache can still be retrieved\n");
+		goto cleanup;
+	}
+
+	res = AST_TEST_PASS;
+
+cleanup:
+	if (cache) {
+		sorcery_memory_cache_close(cache);
+	}
+	if (sorcery) {
+		ast_sorcery_unref(sorcery);
+	}
+
+	return res;
+}
+
+static int check_cache_content(struct ast_test *test, struct ast_sorcery *sorcery, struct sorcery_memory_cache *cache,
+		const char **in_cache, size_t num_in_cache, const char **not_in_cache, size_t num_not_in_cache)
+{
+	int i;
+	int res = 0;
+	RAII_VAR(void *, cached_object, NULL, ao2_cleanup);
+
+	for (i = 0; i < num_in_cache; ++i) {
+		cached_object = sorcery_memory_cache_retrieve_id(sorcery, cache, "test", in_cache[i]);
+		if (!cached_object) {
+			ast_test_status_update(test, "Failed to retrieve '%s' object from the cache\n",
+					in_cache[i]);
+			res = -1;
+		}
+		ao2_ref(cached_object, -1);
+	}
+
+	for (i = 0; i < num_not_in_cache; ++i) {
+		cached_object = sorcery_memory_cache_retrieve_id(sorcery, cache, "test", not_in_cache[i]);
+		if (cached_object) {
+			ast_test_status_update(test, "Retrieved '%s' object from the cache unexpectedly\n",
+					not_in_cache[i]);
+			ao2_ref(cached_object, -1);
+			res = -1;
+		}
+	}
+
+	return res;
+}
+
+AST_TEST_DEFINE(maximum_objects)
+{
+	int res = AST_TEST_FAIL;
+	struct ast_sorcery *sorcery = NULL;
+	struct sorcery_memory_cache *cache = NULL;
+	RAII_VAR(void *, alice, NULL, ao2_cleanup);
+	RAII_VAR(void *, bob, NULL, ao2_cleanup);
+	RAII_VAR(void *, charlie, NULL, ao2_cleanup);
+	RAII_VAR(void *, cached_object, NULL, ao2_cleanup);
+	const char *in_cache[2];
+	const char *not_in_cache[2];
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "maximum_objects";
+		info->category = "/res/res_sorcery_memory_cache/";
+		info->summary = "Ensure that the 'maximum_objects' option works as expected";
+		info->description = "This test performs the following:\n"
+			"\t* Creates a memory cache with maximum_objects=2\n"
+			"\t* Creates a sorcery instance\n"
+			"\t* Creates a three test objects: alice, bob, charlie, and david\n"
+			"\t* Pushes alice and bob into the memory cache\n"
+			"\t* Confirms that alice and bob are in the memory cache\n"
+			"\t* Pushes charlie into the memory cache\n"
+			"\t* Confirms that bob and charlie are in the memory cache\n"
+			"\t* Deletes charlie from the memory cache\n"
+			"\t* Confirms that only bob is in the memory cache\n"
+			"\t* Pushes alice into the memory cache\n"
+			"\t* Confirms that bob and alice are in the memory cache";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	cache = sorcery_memory_cache_open("maximum_objects=2");
+	if (!cache) {
+		ast_test_status_update(test, "Failed to create a sorcery memory cache with maximum_objects=2\n");
+		goto cleanup;
+	}
+
+	if (ao2_container_count(cache->objects)) {
+		ast_test_status_update(test, "Memory cache contains cached objects before we added one\n");
+		goto cleanup;
+	}
+
+	sorcery = alloc_and_initialize_sorcery();
+	if (!sorcery) {
+		ast_test_status_update(test, "Failed to create a test sorcery instance\n");
+		goto cleanup;
+	}
+
+	alice = ast_sorcery_alloc(sorcery, "test", "alice");
+	bob = ast_sorcery_alloc(sorcery, "test", "bob");
+	charlie = ast_sorcery_alloc(sorcery, "test", "charlie");
+
+	if (!alice || !bob || !charlie) {
+		ast_test_status_update(test, "Failed to allocate sorcery object(s)\n");
+		goto cleanup;
+	}
+
+	sorcery_memory_cache_create(sorcery, cache, alice);
+	in_cache[0] = "alice";
+	in_cache[1] = NULL;
+	not_in_cache[0] = "bob";
+	not_in_cache[1] = "charlie";
+	if (check_cache_content(test, sorcery, cache, in_cache, 1, not_in_cache, 2)) {
+		goto cleanup;
+	}
+
+	/* Delays are added to ensure that we are not adding cache entries within the
+	 * same microsecond
+	 */
+	usleep(1000);
+
+	sorcery_memory_cache_create(sorcery, cache, bob);
+	in_cache[0] = "alice";
+	in_cache[1] = "bob";
+	not_in_cache[0] = "charlie";
+	not_in_cache[1] = NULL;
+	if (check_cache_content(test, sorcery, cache, in_cache, 2, not_in_cache, 1)) {
+		goto cleanup;
+	}
+
+	usleep(1000);
+
+	sorcery_memory_cache_create(sorcery, cache, charlie);
+	in_cache[0] = "bob";
+	in_cache[1] = "charlie";
+	not_in_cache[0] = "alice";
+	not_in_cache[1] = NULL;
+	if (check_cache_content(test, sorcery, cache, in_cache, 2, not_in_cache, 1)) {
+		goto cleanup;
+	}
+	usleep(1000);
+
+	sorcery_memory_cache_delete(sorcery, cache, charlie);
+	in_cache[0] = "bob";
+	in_cache[1] = NULL;
+	not_in_cache[0] = "alice";
+	not_in_cache[1] = "charlie";
+	if (check_cache_content(test, sorcery, cache, in_cache, 1, not_in_cache, 2)) {
+		goto cleanup;
+	}
+	usleep(1000);
+
+	sorcery_memory_cache_create(sorcery, cache, alice);
+	in_cache[0] = "bob";
+	in_cache[1] = "alice";
+	not_in_cache[0] = "charlie";
+	not_in_cache[1] = NULL;
+	if (check_cache_content(test, sorcery, cache, in_cache, 2, not_in_cache, 1)) {
+		goto cleanup;
+	}
+
+	res = AST_TEST_PASS;
+
+cleanup:
+	if (cache) {
+		sorcery_memory_cache_close(cache);
+	}
+	if (sorcery) {
+		ast_sorcery_unref(sorcery);
+	}
+
+	return res;
+}
+
+AST_TEST_DEFINE(expiration)
+{
+	int res = AST_TEST_FAIL;
+	struct ast_sorcery *sorcery = NULL;
+	struct sorcery_memory_cache *cache = NULL;
+	int i;
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "expiration";
+		info->category = "/res/res_sorcery_memory_cache/";
+		info->summary = "Add objects to a cache configured with maximum lifetime, confirm they are removed";
+		info->description = "This test performs the following:\n"
+			"\t* Creates a memory cache with a maximum object lifetime of 5 seconds\n"
+			"\t* Pushes 10 objects into the memory cache\n"
+			"\t* Waits (up to) 10 seconds for expiration to occur\n"
+			"\t* Confirms that the objects have been removed from the cache";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	cache = sorcery_memory_cache_open("object_lifetime_maximum=5");
+	if (!cache) {
+		ast_test_status_update(test, "Failed to create a sorcery memory cache using default options\n");
+		goto cleanup;
+	}
+
+	sorcery = alloc_and_initialize_sorcery();
+	if (!sorcery) {
+		ast_test_status_update(test, "Failed to create a test sorcery instance\n");
+		goto cleanup;
+	}
+
+	cache->cache_notify = 1;
+	ast_mutex_init(&cache->lock);
+	ast_cond_init(&cache->cond, NULL);
+
+	for (i = 0; i < 5; ++i) {
+		char uuid[AST_UUID_STR_LEN];
+		void *object;
+
+		object = ast_sorcery_alloc(sorcery, "test", ast_uuid_generate_str(uuid, sizeof(uuid)));
+		if (!object) {
+			ast_test_status_update(test, "Failed to allocate test object for expiration\n");
+			goto cleanup;
+		}
+
+		sorcery_memory_cache_create(sorcery, cache, object);
+
+		ao2_ref(object, -1);
+	}
+
+	ast_mutex_lock(&cache->lock);
+	while (!cache->cache_completed) {
+		struct timeval start = ast_tvnow();
+		struct timespec end = {
+			.tv_sec = start.tv_sec + 10,
+			.tv_nsec = start.tv_usec * 1000,
+		};
+
+		if (ast_cond_timedwait(&cache->cond, &cache->lock, &end) == ETIMEDOUT) {
+			break;
+		}
+	}
+	ast_mutex_unlock(&cache->lock);
+
+	if (ao2_container_count(cache->objects)) {
+		ast_test_status_update(test, "Objects placed into the memory cache did not expire and get removed\n");
+		goto cleanup;
+	}
+
+	res = AST_TEST_PASS;
+
+cleanup:
+	if (cache) {
+		if (cache->cache_notify) {
+			ast_cond_destroy(&cache->cond);
+			ast_mutex_destroy(&cache->lock);
+		}
+		sorcery_memory_cache_close(cache);
+	}
+	if (sorcery) {
+		ast_sorcery_unref(sorcery);
+	}
+
+	return res;
+}
+
+/*!
+ * \brief Backend data that the mock sorcery wizard uses to create objects
+ */
+static struct backend_data {
+	/*! An arbitrary data field */
+	int salt;
+	/*! Another arbitrary data field */
+	int pepper;
+	/*! Indicates whether the backend has data */
+	int exists;
+} *real_backend_data;
+
+/*!
+ * \brief Sorcery object created based on backend data
+ */
+struct test_data {
+	SORCERY_OBJECT(details);
+	/*! Mirrors the backend data's salt field */
+	int salt;
+	/*! Mirrors the backend data's pepper field */
+	int pepper;
+};
+
+/*!
+ * \brief Allocation callback for test_data sorcery object
+ */
+static void *test_data_alloc(const char *id) {
+	return ast_sorcery_generic_alloc(sizeof(struct test_data), NULL);
+}
+
+/*!
+ * \brief Callback for retrieving sorcery object by ID
+ *
+ * The mock wizard uses the \ref real_backend_data in order to construct
+ * objects. If the backend data is "nonexisent" then no object is returned.
+ * Otherwise, an object is created that has the backend data's salt and
+ * pepper values copied.
+ *
+ * \param sorcery The sorcery instance
+ * \param data Unused
+ * \param type The object type. Will always be "test".
+ * \param id The object id. Will always be "test".
+ *
+ * \retval NULL Backend data does not exist
+ * \retval non-NULL An object representing the backend data
+ */
+static void *mock_retrieve_id(const struct ast_sorcery *sorcery, void *data,
+		const char *type, const char *id)
+{
+	struct test_data *b_data;
+
+	if (!real_backend_data->exists) {
+		return NULL;
+	}
+
+	b_data = ast_sorcery_alloc(sorcery, type, id);
+	if (!b_data) {
+		return NULL;
+	}
+
+	b_data->salt = real_backend_data->salt;
+	b_data->pepper = real_backend_data->pepper;
+	return b_data;
+}
+
+/*!
+ * \brief Callback for retrieving multiple sorcery objects
+ *
+ * The mock wizard uses the \ref real_backend_data in order to construct
+ * objects. If the backend data is "nonexisent" then no object is returned.
+ * Otherwise, the number of objects matching the exists value will be returned.
+ *
+ * \param sorcery The sorcery instance
+ * \param data Unused
+ * \param type The object type. Will always be "test".
+ * \param objects Container to place objects into.
+ * \param fields Fields to search for.
+ */
+static void mock_retrieve_multiple(const struct ast_sorcery *sorcery, void *data,
+		const char *type, struct ao2_container *objects, const struct ast_variable *fields)
+{
+	int i;
+
+	if (fields) {
+		return;
+	}
+
+	for (i = 0; i < real_backend_data->exists; ++i) {
+		char uuid[AST_UUID_STR_LEN];
+		struct test_data *b_data;
+
+		b_data = ast_sorcery_alloc(sorcery, type, ast_uuid_generate_str(uuid, sizeof(uuid)));
+		if (!b_data) {
+			continue;
+		}
+
+		b_data->salt = real_backend_data->salt;
+		b_data->pepper = real_backend_data->pepper;
+
+		ao2_link(objects, b_data);
+		ao2_ref(b_data, -1);
+	}
+}
+
+/*!
+ * \brief A mock sorcery wizard used for the stale test
+ */
+static struct ast_sorcery_wizard mock_wizard = {
+	.name = "mock",
+	.retrieve_id = mock_retrieve_id,
+	.retrieve_multiple = mock_retrieve_multiple,
+};
+
+/*!
+ * \brief Wait for the cache to be updated after a stale object is retrieved.
+ *
+ * Since the cache does not know what type of objects it is dealing with, and
+ * since we do not have the internals of the cache, the only way to make this
+ * determination is to continuously retrieve an object from the cache until
+ * we retrieve a different object than we had previously retrieved.
+ *
+ * \param sorcery The sorcery instance
+ * \param previous_object The object we had previously retrieved from the cache
+ * \param[out] new_object The new object we retrieve from the cache
+ *
+ * \retval 0 Successfully retrieved a new object from the cache
+ * \retval non-zero Failed to retrieve a new object from the cache
+ */
+static int wait_for_cache_update(const struct ast_sorcery *sorcery,
+		void *previous_object, struct test_data **new_object)
+{
+	struct timeval start = ast_tvnow();
+
+	while (ast_remaining_ms(start, 5000) > 0) {
+		void *object;
+
+		object = ast_sorcery_retrieve_by_id(sorcery, "test", "test");
+		if (object != previous_object) {
+			*new_object = object;
+			return 0;
+		}
+		ao2_cleanup(object);
+	}
+
+	return -1;
+}
+
+AST_TEST_DEFINE(stale)
+{
+	int res = AST_TEST_FAIL;
+	struct ast_sorcery *sorcery = NULL;
+	struct test_data *backend_object;
+	struct backend_data iterations[] = {
+		{ .salt = 1,      .pepper = 2,       .exists = 1 },
+		{ .salt = 568729, .pepper = -234123, .exists = 1 },
+		{ .salt = 0,      .pepper = 0,       .exists = 0 },
+	};
+	struct backend_data initial = {
+		.salt = 0,
+		.pepper = 0,
+		.exists = 1,
+	};
+	int i;
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "stale";
+		info->category = "/res/res_sorcery_memory_cache/";
+		info->summary = "Ensure that stale objects are replaced with updated objects";
+		info->description = "This test performs the following:\n"
+			"\t* Create a sorcery instance with two wizards"
+			"\t\t* The first is a memory cache that marks items stale after 3 seconds\n"
+			"\t\t* The second is a mock of a back-end\n"
+			"\t* Pre-populates the cache by retrieving some initial data from the backend.\n"
+			"\t* Performs iterations of the following:\n"
+			"\t\t* Update backend data with new values\n"
+			"\t\t* Retrieve item from the cache\n"
+			"\t\t* Ensure the retrieved item does not have the new backend values\n"
+			"\t\t* Wait for cached object to become stale\n"
+			"\t\t* Retrieve the stale cached object\n"
+			"\t\t* Ensure that the stale object retrieved is the same as the fresh one from earlier\n"
+			"\t\t* Wait for the cache to update with new data\n"
+			"\t\t* Ensure that new data in the cache matches backend data";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	ast_sorcery_wizard_register(&mock_wizard);
+
+	sorcery = ast_sorcery_open();
+	if (!sorcery) {
+		ast_test_status_update(test, "Failed to create sorcery instance\n");
+		goto cleanup;
+	}
+
+	ast_sorcery_apply_wizard_mapping(sorcery, "test", "memory_cache",
+			"object_lifetime_stale=3", 1);
+	ast_sorcery_apply_wizard_mapping(sorcery, "test", "mock", NULL, 0);
+	ast_sorcery_internal_object_register(sorcery, "test", test_data_alloc, NULL, NULL);
+
+	/* Prepopulate the cache */
+	real_backend_data = &initial;
+
+	backend_object = ast_sorcery_retrieve_by_id(sorcery, "test", "test");
+	if (!backend_object) {
+		ast_test_status_update(test, "Unable to retrieve backend data and populate the cache\n");
+		goto cleanup;
+	}
+	ao2_ref(backend_object, -1);
+
+	for (i = 0; i < ARRAY_LEN(iterations); ++i) {
+		RAII_VAR(struct test_data *, cache_fresh, NULL, ao2_cleanup);
+		RAII_VAR(struct test_data *, cache_stale, NULL, ao2_cleanup);
+		RAII_VAR(struct test_data *, cache_new, NULL, ao2_cleanup);
+
+		real_backend_data = &iterations[i];
+
+		ast_test_status_update(test, "Begininning iteration %d\n", i);
+
+		cache_fresh = ast_sorcery_retrieve_by_id(sorcery, "test", "test");
+		if (!cache_fresh) {
+			ast_test_status_update(test, "Unable to retrieve fresh cached object\n");
+			goto cleanup;
+		}
+
+		if (cache_fresh->salt == iterations[i].salt || cache_fresh->pepper == iterations[i].pepper) {
+			ast_test_status_update(test, "Fresh cached object has unexpected values. Did we hit the backend?\n");
+			goto cleanup;
+		}
+
+		sleep(5);
+
+		cache_stale = ast_sorcery_retrieve_by_id(sorcery, "test", "test");
+		if (!cache_stale) {
+			ast_test_status_update(test, "Unable to retrieve stale cached object\n");
+			goto cleanup;
+		}
+
+		if (cache_stale != cache_fresh) {
+			ast_test_status_update(test, "Stale cache hit retrieved different object than fresh cache hit\n");
+			goto cleanup;
+		}
+
+		if (wait_for_cache_update(sorcery, cache_stale, &cache_new)) {
+			ast_test_status_update(test, "Cache was not updated\n");
+			goto cleanup;
+		}
+
+		if (iterations[i].exists) {
+			if (!cache_new) {
+				ast_test_status_update(test, "Failed to retrieve item from cache when there should be one present\n");
+				goto cleanup;
+			} else if (cache_new->salt != iterations[i].salt ||
+					cache_new->pepper != iterations[i].pepper) {
+				ast_test_status_update(test, "New cached item has unexpected values\n");
+				goto cleanup;
+			}
+		} else if (cache_new) {
+			ast_test_status_update(test, "Retrieved a cached item when there should not have been one present\n");
+			goto cleanup;
+		}
+	}
+
+	res = AST_TEST_PASS;
+
+cleanup:
+	if (sorcery) {
+		ast_sorcery_unref(sorcery);
+	}
+	ast_sorcery_wizard_unregister(&mock_wizard);
+	return res;
+}
+
+AST_TEST_DEFINE(full_backend_cache_expiration)
+{
+	int res = AST_TEST_FAIL;
+	struct ast_sorcery *sorcery = NULL;
+	struct backend_data initial = {
+		.salt = 0,
+		.pepper = 0,
+		.exists = 4,
+	};
+	struct ao2_container *objects;
+	ast_mutex_t lock;
+	ast_cond_t cond;
+	struct timeval start;
+	struct timespec end;
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "full_backend_cache_expiration";
+		info->category = "/res/res_sorcery_memory_cache/";
+		info->summary = "Ensure that the full backend cache actually caches the backend";
+		info->description = "This test performs the following:\n"
+			"\t* Create a sorcery instance with two wizards"
+			"\t\t* The first is a memory cache that expires objects after 3 seconds and does full backend caching\n"
+			"\t\t* The second is a mock of a back-end\n"
+			"\t* Populates the cache by requesting all objects which returns 4.\n"
+			"\t* Updates the backend to contain a different number of objects, 8.\n"
+			"\t* Requests all objects and confirms the number returned is only 4.\n"
+			"\t* Wait for cached objects to expire.\n"
+			"\t* Requests all objects and confirms the number returned is 8.";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	ast_sorcery_wizard_register(&mock_wizard);
+
+	sorcery = ast_sorcery_open();
+	if (!sorcery) {
+		ast_test_status_update(test, "Failed to create sorcery instance\n");
+		goto cleanup;
+	}
+
+	ast_sorcery_apply_wizard_mapping(sorcery, "test", "memory_cache",
+			"object_lifetime_maximum=3,full_backend_cache=yes", 1);
+	ast_sorcery_apply_wizard_mapping(sorcery, "test", "mock", NULL, 0);
+	ast_sorcery_internal_object_register(sorcery, "test", test_data_alloc, NULL, NULL);
+	ast_sorcery_object_field_register_nodoc(sorcery, "test", "salt", "0", OPT_UINT_T, 0, FLDSET(struct test_data, salt));
+	ast_sorcery_object_field_register_nodoc(sorcery, "test", "pepper", "0", OPT_UINT_T, 0, FLDSET(struct test_data, pepper));
+
+	/* Prepopulate the cache */
+	real_backend_data = &initial;
+
+	/* Get all current objects in the backend */
+	objects = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+	if (!objects) {
+		ast_test_status_update(test, "Unable to retrieve all objects in backend and populate cache\n");
+		goto cleanup;
+	}
+	ao2_ref(objects, -1);
+
+	/* Update the backend to have a different number of objects */
+	initial.exists = 8;
+
+	/* Get all current objects in the backend */
+	objects = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+	if (!objects) {
+		ast_test_status_update(test, "Unable to retrieve all objects in backend and populate cache\n");
+		goto cleanup;
+	}
+
+	if (ao2_container_count(objects) == initial.exists) {
+		ast_test_status_update(test, "Number of objects returned is of the current backend and not the cache\n");
+		ao2_ref(objects, -1);
+		goto cleanup;
+	}
+
+	ao2_ref(objects, -1);
+
+	ast_mutex_init(&lock);
+	ast_cond_init(&cond, NULL);
+
+	start = ast_tvnow();
+	end.tv_sec = start.tv_sec + 5;
+	end.tv_nsec = start.tv_usec * 1000;
+
+	ast_mutex_lock(&lock);
+	while (ast_cond_timedwait(&cond, &lock, &end) != ETIMEDOUT) {
+	}
+	ast_mutex_unlock(&lock);
+
+	ast_mutex_destroy(&lock);
+	ast_cond_destroy(&cond);
+
+	/* Get all current objects in the backend */
+	objects = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+	if (!objects) {
+		ast_test_status_update(test, "Unable to retrieve all objects in backend and populate cache\n");
+		goto cleanup;
+	}
+
+	if (ao2_container_count(objects) != initial.exists) {
+		ast_test_status_update(test, "Number of objects returned is NOT of the current backend when it should be\n");
+		ao2_ref(objects, -1);
+		goto cleanup;
+	}
+
+	ao2_ref(objects, -1);
+
+	res = AST_TEST_PASS;
+
+cleanup:
+	if (sorcery) {
+		ast_sorcery_unref(sorcery);
+	}
+	ast_sorcery_wizard_unregister(&mock_wizard);
+	return res;
+}
+
+AST_TEST_DEFINE(full_backend_cache_stale)
+{
+	int res = AST_TEST_FAIL;
+	struct ast_sorcery *sorcery = NULL;
+	struct backend_data initial = {
+		.salt = 0,
+		.pepper = 0,
+		.exists = 4,
+	};
+	struct ao2_container *objects;
+	ast_mutex_t lock;
+	ast_cond_t cond;
+	struct timeval start;
+	struct timespec end;
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "full_backend_cache_stale";
+		info->category = "/res/res_sorcery_memory_cache/";
+		info->summary = "Ensure that the full backend cache works with staleness";
+		info->description = "This test performs the following:\n"
+			"\t* Create a sorcery instance with two wizards"
+			"\t\t* The first is a memory cache that stales objects after 1 second and does full backend caching\n"
+			"\t\t* The second is a mock of a back-end\n"
+			"\t* Populates the cache by requesting all objects which returns 4.\n"
+			"\t* Wait for objects to go stale.\n"
+			"\t* Updates the backend to contain a different number of objects, 8.\""
+			"\t* Requests all objects and confirms the number returned is only 4.\n"
+			"\t* Wait for objects to be refreshed from backend.\n"
+			"\t* Requests all objects and confirms the number returned is 8.";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	ast_sorcery_wizard_register(&mock_wizard);
+
+	ast_mutex_init(&lock);
+	ast_cond_init(&cond, NULL);
+
+	sorcery = ast_sorcery_open();
+	if (!sorcery) {
+		ast_test_status_update(test, "Failed to create sorcery instance\n");
+		goto cleanup;
+	}
+
+	ast_sorcery_apply_wizard_mapping(sorcery, "test", "memory_cache",
+			"object_lifetime_stale=1,full_backend_cache=yes", 1);
+	ast_sorcery_apply_wizard_mapping(sorcery, "test", "mock", NULL, 0);
+	ast_sorcery_internal_object_register(sorcery, "test", test_data_alloc, NULL, NULL);
+	ast_sorcery_object_field_register_nodoc(sorcery, "test", "salt", "0", OPT_UINT_T, 0, FLDSET(struct test_data, salt));
+	ast_sorcery_object_field_register_nodoc(sorcery, "test", "pepper", "0", OPT_UINT_T, 0, FLDSET(struct test_data, pepper));
+
+	/* Prepopulate the cache */
+	real_backend_data = &initial;
+
+	/* Get all current objects in the backend */
+	objects = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+	if (!objects) {
+		ast_test_status_update(test, "Unable to retrieve all objects in backend and populate cache\n");
+		goto cleanup;
+	}
+	ao2_ref(objects, -1);
+
+	start = ast_tvnow();
+	end.tv_sec = start.tv_sec + 5;
+	end.tv_nsec = start.tv_usec * 1000;
+
+	ast_mutex_lock(&lock);
+	while (ast_cond_timedwait(&cond, &lock, &end) != ETIMEDOUT) {
+	}
+	ast_mutex_unlock(&lock);
+
+	initial.exists = 8;
+
+	/* Get all current objects in the backend */
+	objects = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+	if (!objects) {
+		ast_test_status_update(test, "Unable to retrieve all objects in backend and populate cache\n");
+		goto cleanup;
+	}
+
+	if (ao2_container_count(objects) == initial.exists) {
+		ast_test_status_update(test, "Number of objects returned is of the backend and not the cache\n");
+		ao2_ref(objects, -1);
+		goto cleanup;
+	}
+
+	ao2_ref(objects, -1);
+
+	start = ast_tvnow();
+	end.tv_sec = start.tv_sec + 5;
+	end.tv_nsec = start.tv_usec * 1000;
+
+	ast_mutex_lock(&lock);
+	while (ast_cond_timedwait(&cond, &lock, &end) != ETIMEDOUT) {
+	}
+	ast_mutex_unlock(&lock);
+
+	/* Get all current objects in the backend */
+	objects = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+	if (!objects) {
+		ast_test_status_update(test, "Unable to retrieve all objects in backend and populate cache\n");
+		goto cleanup;
+	}
+
+	if (ao2_container_count(objects) != initial.exists) {
+		ast_test_status_update(test, "Number of objects returned is not of backend\n");
+		ao2_ref(objects, -1);
+		goto cleanup;
+	}
+
+	ao2_ref(objects, -1);
+
+	start = ast_tvnow();
+	end.tv_sec = start.tv_sec + 5;
+	end.tv_nsec = start.tv_usec * 1000;
+
+	ast_mutex_lock(&lock);
+	while (ast_cond_timedwait(&cond, &lock, &end) != ETIMEDOUT) {
+	}
+	ast_mutex_unlock(&lock);
+
+	res = AST_TEST_PASS;
+
+cleanup:
+	if (sorcery) {
+		ast_sorcery_unref(sorcery);
+	}
+	ast_sorcery_wizard_unregister(&mock_wizard);
+	ast_mutex_destroy(&lock);
+	ast_cond_destroy(&cond);
+	return res;
+}
+
+#endif
+
+static int unload_module(void)
+{
+	AST_TEST_UNREGISTER(open_with_valid_options);
+	AST_TEST_UNREGISTER(open_with_invalid_options);
+	AST_TEST_UNREGISTER(create_and_retrieve);
+	AST_TEST_UNREGISTER(update);
+	AST_TEST_UNREGISTER(delete);
+	AST_TEST_UNREGISTER(maximum_objects);
+	AST_TEST_UNREGISTER(expiration);
+	AST_TEST_UNREGISTER(stale);
+	AST_TEST_UNREGISTER(full_backend_cache_expiration);
+	AST_TEST_UNREGISTER(full_backend_cache_stale);
+
+	ast_manager_unregister("SorceryMemoryCacheExpireObject");
+	ast_manager_unregister("SorceryMemoryCacheExpire");
+	ast_manager_unregister("SorceryMemoryCacheStaleObject");
+	ast_manager_unregister("SorceryMemoryCacheStale");
+	ast_manager_unregister("SorceryMemoryCachePopulate");
+
+	ast_cli_unregister_multiple(cli_memory_cache, ARRAY_LEN(cli_memory_cache));
+
+	ast_sorcery_wizard_unregister(&memory_cache_object_wizard);
+
+	/*
+	 * XXX There is the potential to leak memory if there are pending
+	 * next-cache-expiration and stale-cache-update tasks in the scheduler.
+	 */
+	if (sched) {
+		ast_sched_context_destroy(sched);
+		sched = NULL;
+	}
+
+	ao2_cleanup(caches);
+	caches = NULL;
+
+	return 0;
+}
+
+static int load_module(void)
+{
+	int res;
+
+	caches = ao2_container_alloc(CACHES_CONTAINER_BUCKET_SIZE, sorcery_memory_cache_hash,
+		sorcery_memory_cache_cmp);
+	if (!caches) {
+		ast_log(LOG_ERROR, "Failed to create container for configured caches\n");
+		unload_module();
+		return AST_MODULE_LOAD_DECLINE;
+	}
+
+	sched = ast_sched_context_create();
+	if (!sched) {
+		ast_log(LOG_ERROR, "Failed to create scheduler for cache management\n");
+		unload_module();
+		return AST_MODULE_LOAD_DECLINE;
+	}
+
+	if (ast_sched_start_thread(sched)) {
+		ast_log(LOG_ERROR, "Failed to create scheduler thread for cache management\n");
+		unload_module();
+		return AST_MODULE_LOAD_DECLINE;
+	}
+
+	if (ast_sorcery_wizard_register(&memory_cache_object_wizard)) {
+		unload_module();
+		return AST_MODULE_LOAD_DECLINE;
+	}
+
+	res = ast_cli_register_multiple(cli_memory_cache, ARRAY_LEN(cli_memory_cache));
+	res |= ast_manager_register_xml("SorceryMemoryCacheExpireObject", EVENT_FLAG_SYSTEM, sorcery_memory_cache_ami_expire_object);
+	res |= ast_manager_register_xml("SorceryMemoryCacheExpire", EVENT_FLAG_SYSTEM, sorcery_memory_cache_ami_expire);
+	res |= ast_manager_register_xml("SorceryMemoryCacheStaleObject", EVENT_FLAG_SYSTEM, sorcery_memory_cache_ami_stale_object);
+	res |= ast_manager_register_xml("SorceryMemoryCacheStale", EVENT_FLAG_SYSTEM, sorcery_memory_cache_ami_stale);
+	res |= ast_manager_register_xml("SorceryMemoryCachePopulate", EVENT_FLAG_SYSTEM, sorcery_memory_cache_ami_populate);
+
+	if (res) {
+		unload_module();
+		return AST_MODULE_LOAD_DECLINE;
+	}
+
+	/* This causes the stale unit test to execute last, so if a sorcery instance persists
+	 * longer than expected subsequent unit tests don't fail when setting it up.
+	 */
+	AST_TEST_REGISTER(stale);
+	AST_TEST_REGISTER(open_with_valid_options);
+	AST_TEST_REGISTER(open_with_invalid_options);
+	AST_TEST_REGISTER(create_and_retrieve);
+	AST_TEST_REGISTER(update);
+	AST_TEST_REGISTER(delete);
+	AST_TEST_REGISTER(maximum_objects);
+	AST_TEST_REGISTER(expiration);
+	AST_TEST_REGISTER(full_backend_cache_expiration);
+	AST_TEST_REGISTER(full_backend_cache_stale);
+
+	return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Sorcery Memory Cache Object Wizard",
+	.support_level = AST_MODULE_SUPPORT_CORE,
+	.load = load_module,
+	.unload = unload_module,
+	.load_pri = AST_MODPRI_REALTIME_DRIVER,
+);
diff --git a/res/res_sorcery_realtime.c b/res/res_sorcery_realtime.c
index b944bfd..b16069b 100644
--- a/res/res_sorcery_realtime.c
+++ b/res/res_sorcery_realtime.c
@@ -30,7 +30,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 425384 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <regex.h>
 
@@ -142,6 +142,8 @@ static struct ast_variable *sorcery_realtime_filter_objectset(struct ast_variabl
 		}
 	}
 
+	ao2_cleanup(object_type);
+
 	return objectset;
 }
 
@@ -158,7 +160,9 @@ static void *sorcery_realtime_retrieve_fields(const struct ast_sorcery *sorcery,
 
 	objectset = sorcery_realtime_filter_objectset(objectset, &id, sorcery, type);
 
-	if (!id || !(object = ast_sorcery_alloc(sorcery, type, id->value)) || ast_sorcery_objectset_apply(sorcery, object, objectset)) {
+	if (!id
+		|| !(object = ast_sorcery_alloc(sorcery, type, id->value))
+		|| ast_sorcery_objectset_apply(sorcery, object, objectset)) {
 		return NULL;
 	}
 
@@ -214,16 +218,16 @@ static void sorcery_realtime_retrieve_multiple(const struct ast_sorcery *sorcery
 
 static void sorcery_realtime_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *regex)
 {
-	char field[strlen(UUID_FIELD) + 6], value[strlen(regex) + 2];
+	char field[strlen(UUID_FIELD) + 6], value[strlen(regex) + 3];
 	RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
 
 	/* The realtime API provides no direct ability to do regex so for now we support a limited subset using pattern matching */
-	if (regex[0] != '^') {
-		return;
-	}
-
 	snprintf(field, sizeof(field), "%s LIKE", UUID_FIELD);
-	snprintf(value, sizeof(value), "%s%%", regex + 1);
+	if (regex[0] == '^') {
+		snprintf(value, sizeof(value), "%s%%", regex + 1);
+	} else {
+		snprintf(value, sizeof(value), "%%%s%%", regex);
+	}
 
 	if (!(fields = ast_variable_new(field, value, ""))) {
 		return;
diff --git a/res/res_speech.c b/res/res_speech.c
index 6c56e25..7d75985 100644
--- a/res/res_speech.c
+++ b/res/res_speech.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $");
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
 
 #include "asterisk/channel.h"
 #include "asterisk/module.h"
diff --git a/res/res_srtp.c b/res/res_srtp.c
index 07ea878..8d8daf0 100644
--- a/res/res_srtp.c
+++ b/res/res_srtp.c
@@ -37,7 +37,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 426143 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <srtp/srtp.h>
 #include <srtp/crypto_kernel.h>
diff --git a/res/res_stasis.c b/res/res_stasis.c
index f954d42..94b037e 100644
--- a/res/res_stasis.c
+++ b/res/res_stasis.c
@@ -53,7 +53,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 429062 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/astobj2.h"
 #include "asterisk/callerid.h"
@@ -109,6 +109,11 @@ struct ao2_container *app_bridges_moh;
 
 struct ao2_container *app_bridges_playback;
 
+/*!
+ * \internal \brief List of registered event sources.
+ */
+AST_RWLIST_HEAD_STATIC(event_sources, stasis_app_event_source);
+
 static struct ast_json *stasis_end_to_json(struct stasis_message *message,
 		const struct stasis_message_sanitizer *sanitize)
 {
@@ -145,10 +150,10 @@ static struct ast_json *stasis_start_to_json(struct stasis_message *message,
 		return NULL;
 	}
 
-	msg = ast_json_pack("{s: s, s: O, s: O, s: o}",
+	msg = ast_json_pack("{s: s, s: o, s: o, s: o}",
 		"type", "StasisStart",
-		"timestamp", ast_json_object_get(payload->blob, "timestamp"),
-		"args", ast_json_object_get(payload->blob, "args"),
+		"timestamp", ast_json_copy(ast_json_object_get(payload->blob, "timestamp")),
+		"args", ast_json_deep_copy(ast_json_object_get(payload->blob, "args")),
 		"channel", ast_channel_snapshot_to_json(payload->channel, NULL));
 	if (!msg) {
 		ast_log(LOG_ERROR, "Failed to pack JSON for StasisStart message\n");
@@ -1280,6 +1285,7 @@ int stasis_app_exec(struct ast_channel *chan, const char *app_name, int argc,
 
 		/* Check to see if a bridge absorbed our hangup frame */
 		if (ast_check_hangup_locked(chan)) {
+			control_mark_done(control);
 			break;
 		}
 
@@ -1288,7 +1294,9 @@ int stasis_app_exec(struct ast_channel *chan, const char *app_name, int argc,
 
 		if (bridge != last_bridge) {
 			app_unsubscribe_bridge(app, last_bridge);
-			app_subscribe_bridge(app, bridge);
+			if (bridge) {
+				app_subscribe_bridge(app, bridge);
+			}
 		}
 
 		if (bridge) {
@@ -1303,6 +1311,7 @@ int stasis_app_exec(struct ast_channel *chan, const char *app_name, int argc,
 		if (r < 0) {
 			ast_debug(3, "%s: Poll error\n",
 				  ast_channel_uniqueid(chan));
+			control_mark_done(control);
 			break;
 		}
 
@@ -1323,6 +1332,7 @@ int stasis_app_exec(struct ast_channel *chan, const char *app_name, int argc,
 			/* Continue on in the dialplan */
 			ast_debug(3, "%s: Hangup (no more frames)\n",
 				ast_channel_uniqueid(chan));
+			control_mark_done(control);
 			break;
 		}
 
@@ -1331,13 +1341,14 @@ int stasis_app_exec(struct ast_channel *chan, const char *app_name, int argc,
 				/* Continue on in the dialplan */
 				ast_debug(3, "%s: Hangup\n",
 					ast_channel_uniqueid(chan));
+				control_mark_done(control);
 				break;
 			}
 		}
 	}
 
 	ast_channel_lock(chan);
-	needs_depart = ast_channel_is_bridged(chan);
+	needs_depart = (ast_channel_internal_bridge_channel(chan) != NULL);
 	ast_channel_unlock(chan);
 	if (needs_depart) {
 		ast_bridge_depart(chan);
@@ -1373,13 +1384,26 @@ int stasis_app_exec(struct ast_channel *chan, const char *app_name, int argc,
 	control_unlink(control);
 	control = NULL;
 
-	if (!ast_check_hangup_locked(chan) && !ast_channel_pbx(chan)) {
-		struct ast_pbx_args pbx_args;
+	if (!ast_channel_pbx(chan)) {
+		int chan_hungup;
+
+		/* The ASYNCGOTO softhangup flag may have broken the channel out of
+		 * its bridge to run dialplan, so if there's no pbx on the channel
+		 * let it run dialplan here. Otherwise, it will run when this
+		 * application exits. */
+		ast_channel_lock(chan);
+		ast_channel_clear_softhangup(chan, AST_SOFTHANGUP_ASYNCGOTO);
+		chan_hungup = ast_check_hangup(chan);
+		ast_channel_unlock(chan);
 
-		memset(&pbx_args, 0, sizeof(pbx_args));
-		pbx_args.no_hangup_chan = 1;
+		if (!chan_hungup) {
+			struct ast_pbx_args pbx_args;
 
-		res = ast_pbx_run_args(chan, &pbx_args);
+			memset(&pbx_args, 0, sizeof(pbx_args));
+			pbx_args.no_hangup_chan = 1;
+
+			res = ast_pbx_run_args(chan, &pbx_args);
+		}
 	}
 
 	return res;
@@ -1452,7 +1476,7 @@ struct ao2_container *stasis_app_get_all(void)
 	return ao2_bump(apps);
 }
 
-int stasis_app_register(const char *app_name, stasis_app_cb handler, void *data)
+static int __stasis_app_register(const char *app_name, stasis_app_cb handler, void *data, int all_events)
 {
 	RAII_VAR(struct stasis_app *, app, NULL, ao2_cleanup);
 
@@ -1465,8 +1489,20 @@ int stasis_app_register(const char *app_name, stasis_app_cb handler, void *data)
 	if (app) {
 		app_update(app, handler, data);
 	} else {
-		app = app_create(app_name, handler, data);
+		app = app_create(app_name, handler, data, all_events ? STASIS_APP_SUBSCRIBE_ALL : STASIS_APP_SUBSCRIBE_MANUAL);
 		if (app) {
+			if (all_events) {
+				struct stasis_app_event_source *source;
+				SCOPED_LOCK(lock, &event_sources, AST_RWLIST_RDLOCK, AST_RWLIST_UNLOCK);
+
+				AST_LIST_TRAVERSE(&event_sources, source, next) {
+					if (!source->subscribe) {
+						continue;
+					}
+
+					source->subscribe(app, NULL);
+				}
+			}
 			ao2_link_flags(apps_registry, app, OBJ_NOLOCK);
 		} else {
 			ao2_unlock(apps_registry);
@@ -1482,6 +1518,16 @@ int stasis_app_register(const char *app_name, stasis_app_cb handler, void *data)
 	return 0;
 }
 
+int stasis_app_register(const char *app_name, stasis_app_cb handler, void *data)
+{
+	return __stasis_app_register(app_name, handler, data, 0);
+}
+
+int stasis_app_register_all(const char *app_name, stasis_app_cb handler, void *data)
+{
+	return __stasis_app_register(app_name, handler, data, 1);
+}
+
 void stasis_app_unregister(const char *app_name)
 {
 	RAII_VAR(struct stasis_app *, app, NULL, ao2_cleanup);
@@ -1509,11 +1555,6 @@ void stasis_app_unregister(const char *app_name)
 	cleanup();
 }
 
-/*!
- * \internal \brief List of registered event sources.
- */
-AST_RWLIST_HEAD_STATIC(event_sources, stasis_app_event_source);
-
 void stasis_app_register_event_source(struct stasis_app_event_source *obj)
 {
 	SCOPED_LOCK(lock, &event_sources, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK);
@@ -1710,8 +1751,8 @@ static enum stasis_app_subscribe_res app_subscribe(
 
 	ast_debug(3, "%s: Checking %s\n", app_name, uri);
 
-	if (!event_source->find ||
-	    (!(obj = event_source->find(app, uri + strlen(event_source->scheme))))) {
+	if (!ast_strlen_zero(uri + strlen(event_source->scheme)) &&
+	    (!event_source->find || (!(obj = event_source->find(app, uri + strlen(event_source->scheme)))))) {
 		ast_log(LOG_WARNING, "Event source not found: %s\n", uri);
 		return STASIS_ASR_EVENT_SOURCE_NOT_FOUND;
 	}
@@ -1787,7 +1828,7 @@ enum stasis_app_user_event_res stasis_app_user_event(const char *app_name,
 	RAII_VAR(struct ast_multi_object_blob *, multi, NULL, ao2_cleanup);
 	RAII_VAR(void *, obj, NULL, ao2_cleanup);
 	RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
-	enum stasis_app_subscribe_res res = STASIS_APP_USER_INTERNAL_ERROR;
+	enum stasis_app_user_event_res res = STASIS_APP_USER_INTERNAL_ERROR;
 	struct ast_json *json_value;
 	int have_channel = 0;
 	int i;
@@ -1804,6 +1845,8 @@ enum stasis_app_user_event_res stasis_app_user_event(const char *app_name,
 	blob = json_variables;
 	if (!blob) {
 		blob = ast_json_pack("{}");
+	} else {
+		ast_json_ref(blob);
 	}
 	json_value = ast_json_string_create(event_name);
 	if (!json_value) {
@@ -2043,6 +2086,7 @@ static int load_module(void)
 }
 
 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Stasis application support",
+	.load_pri = AST_MODPRI_APP_DEPEND,
 	.support_level = AST_MODULE_SUPPORT_CORE,
 	.load = load_module,
 	.unload = unload_module,
diff --git a/res/res_stasis_answer.c b/res/res_stasis_answer.c
index ccaab2a..05d5302 100644
--- a/res/res_stasis_answer.c
+++ b/res/res_stasis_answer.c
@@ -30,7 +30,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 421880 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/stasis_app_impl.h"
diff --git a/res/res_stasis_device_state.c b/res/res_stasis_device_state.c
index bc56232..c0b6859 100644
--- a/res/res_stasis_device_state.c
+++ b/res/res_stasis_device_state.c
@@ -23,7 +23,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428687 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/astdb.h"
 #include "asterisk/astobj2.h"
@@ -44,6 +44,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428687 $")
 /*! Number of hash buckets for device state subscriptions */
 #define DEVICE_STATE_BUCKETS 37
 
+/*! The key used for tracking a subscription to all device states */
+#define DEVICE_STATE_ALL "__AST_DEVICE_STATE_ALL_TOPIC"
+
 /*! Container for subscribed device states */
 static struct ao2_container *device_state_subscriptions;
 
@@ -105,18 +108,24 @@ 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(sub->sub);
+	sub->sub = stasis_unsubscribe_and_join(sub->sub);
 	ast_string_field_free_memory(sub);
 }
 
 static struct device_state_subscription *device_state_subscription_create(
 	const struct stasis_app *app, const char *device_name)
 {
-	struct device_state_subscription *sub = ao2_alloc(
-		sizeof(*sub), device_state_subscription_destroy);
+	struct device_state_subscription *sub;
 	const char *app_name = stasis_app_name(app);
-	size_t size = strlen(device_name) + strlen(app_name) + 2;
+	size_t size;
+
+	if (ast_strlen_zero(device_name)) {
+		device_name = DEVICE_STATE_ALL;
+	}
 
+	size = strlen(device_name) + strlen(app_name) + 2;
+
+ 	sub = ao2_alloc(sizeof(*sub), device_state_subscription_destroy);
 	if (!sub) {
 		return NULL;
 	}
@@ -314,25 +323,50 @@ static void *find_device_state(const struct stasis_app *app, const char *name)
 
 static int is_subscribed_device_state(struct stasis_app *app, const char *name)
 {
-	RAII_VAR(struct device_state_subscription *, sub,
-		 find_device_state_subscription(app, name), ao2_cleanup);
-	return sub != NULL;
+	struct device_state_subscription *sub;
+
+	sub = find_device_state_subscription(app, DEVICE_STATE_ALL);
+	if (sub) {
+		ao2_ref(sub, -1);
+		return 1;
+	}
+
+	sub = find_device_state_subscription(app, name);
+	if (sub) {
+		ao2_ref(sub, -1);
+		return 1;
+	}
+
+	return 0;
 }
 
 static int subscribe_device_state(struct stasis_app *app, void *obj)
 {
 	struct device_state_subscription *sub = obj;
+	struct stasis_topic *topic;
+
+	if (!sub) {
+		sub = device_state_subscription_create(app, NULL);
+		if (!sub) {
+			return -1;
+		}
+	}
 
-	ast_debug(3, "Subscribing to device %s", sub->device_name);
+	if (strcmp(sub->device_name, DEVICE_STATE_ALL)) {
+		topic = ast_device_state_topic(sub->device_name);
+	} else {
+		topic = ast_device_state_topic_all();
+	}
 
 	if (is_subscribed_device_state(app, sub->device_name)) {
 		ast_debug(3, "App %s is already subscribed to %s\n", stasis_app_name(app), sub->device_name);
 		return 0;
 	}
 
-	if (!(sub->sub = stasis_subscribe_pool(
-			ast_device_state_topic(sub->device_name),
-			device_state_cb, sub))) {
+	ast_debug(3, "Subscribing to device %s\n", sub->device_name);
+
+	sub->sub = stasis_subscribe_pool(topic, device_state_cb, sub);
+	if (!sub->sub) {
 		ast_log(LOG_ERROR, "Unable to subscribe to device %s\n",
 			sub->device_name);
 		return -1;
diff --git a/res/res_stasis_mailbox.c b/res/res_stasis_mailbox.c
index 2d53db0..d061d70 100644
--- a/res/res_stasis_mailbox.c
+++ b/res/res_stasis_mailbox.c
@@ -24,7 +24,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/astdb.h"
 #include "asterisk/astobj2.h"
diff --git a/res/res_stasis_playback.c b/res/res_stasis_playback.c
index b6d6f2a..779dd77 100644
--- a/res/res_stasis_playback.c
+++ b/res/res_stasis_playback.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 421880 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/app.h"
 #include "asterisk/astobj2.h"
@@ -105,9 +105,9 @@ static struct ast_json *playback_to_json(struct stasis_message *message,
 		return NULL;
 	}
 
-	return ast_json_pack("{s: s, s: O}",
+	return ast_json_pack("{s: s, s: o}",
 		"type", type,
-		"playback", blob);
+		"playback", ast_json_deep_copy(blob));
 }
 
 STASIS_MESSAGE_TYPE_DEFN(stasis_app_playback_snapshot_type,
@@ -629,9 +629,9 @@ enum stasis_playback_oper_results stasis_app_playback_operation(
 	playback_opreation_cb cb;
 	SCOPED_AO2LOCK(lock, playback);
 
-	ast_assert(playback->state >= 0 && playback->state < STASIS_PLAYBACK_STATE_MAX);
+	ast_assert((unsigned int)playback->state < STASIS_PLAYBACK_STATE_MAX);
 
-	if (operation < 0 || operation >= STASIS_PLAYBACK_MEDIA_OP_MAX) {
+	if (operation >= STASIS_PLAYBACK_MEDIA_OP_MAX) {
 		ast_log(LOG_ERROR, "Invalid playback operation %u\n", operation);
 		return -1;
 	}
diff --git a/res/res_stasis_recording.c b/res/res_stasis_recording.c
index ad8158a..392d92c 100644
--- a/res/res_stasis_recording.c
+++ b/res/res_stasis_recording.c
@@ -30,7 +30,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 421880 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/dsp.h"
 #include "asterisk/file.h"
@@ -91,9 +91,9 @@ static struct ast_json *recording_to_json(struct stasis_message *message,
 		return NULL;
 	}
 
-	return ast_json_pack("{s: s, s: O}",
+	return ast_json_pack("{s: s, s: o}",
 		"type", type,
-		"recording", blob);
+		"recording", ast_json_deep_copy(blob));
 }
 
 STASIS_MESSAGE_TYPE_DEFN(stasis_app_recording_snapshot_type,
@@ -212,7 +212,7 @@ enum ast_record_if_exists stasis_app_recording_if_exists_parse(
 		return AST_RECORD_IF_EXISTS_APPEND;
 	}
 
-	return -1;
+	return AST_RECORD_IF_EXISTS_ERROR;
 }
 
 static void recording_publish(struct stasis_app_recording *recording, const char *cause)
@@ -595,13 +595,13 @@ enum stasis_app_recording_oper_results stasis_app_recording_operation(
 	recording_operation_cb cb;
 	SCOPED_AO2LOCK(lock, recording);
 
-	if (recording->state < 0 || recording->state >= STASIS_APP_RECORDING_STATE_MAX) {
+	if ((unsigned int)recording->state >= STASIS_APP_RECORDING_STATE_MAX) {
 		ast_log(LOG_WARNING, "Invalid recording state %u\n",
 			recording->state);
 		return -1;
 	}
 
-	if (operation < 0 || operation >= STASIS_APP_RECORDING_OPER_MAX) {
+	if ((unsigned int)operation >= STASIS_APP_RECORDING_OPER_MAX) {
 		ast_log(LOG_WARNING, "Invalid recording operation %u\n",
 			operation);
 		return -1;
diff --git a/res/res_stasis_snoop.c b/res/res_stasis_snoop.c
index 4722bb4..abdef6e 100644
--- a/res/res_stasis_snoop.c
+++ b/res/res_stasis_snoop.c
@@ -30,7 +30,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420879 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/stasis_app_impl.h"
@@ -177,12 +177,28 @@ static struct ast_frame *snoop_read(struct ast_channel *chan)
 	}
 
 	/* Only get audio from the spy audiohook if it is active */
-	if (snoop->spy_active) {
-		ast_audiohook_lock(&snoop->spy);
-		frame = ast_audiohook_read_frame(&snoop->spy, snoop->spy_samples, snoop->spy_direction, snoop->spy_format);
-		ast_audiohook_unlock(&snoop->spy);
+	if (!snoop->spy_active) {
+		return &ast_null_frame;
 	}
 
+	ast_audiohook_lock(&snoop->spy);
+	if (snoop->spy_direction != AST_AUDIOHOOK_DIRECTION_BOTH) {
+		/*
+		 * When a singular direction is chosen frames are still written to the
+		 * opposing direction's queue. Those frames must be read so the queue
+		 * does not continue to grow, however since they are not needed for the
+		 * selected direction they can be dropped.
+		 */
+		enum ast_audiohook_direction opposing_direction =
+			snoop->spy_direction == AST_AUDIOHOOK_DIRECTION_READ ?
+			AST_AUDIOHOOK_DIRECTION_WRITE : AST_AUDIOHOOK_DIRECTION_READ;
+		ast_frame_dtor(ast_audiohook_read_frame(&snoop->spy, snoop->spy_samples,
+							opposing_direction, snoop->spy_format));
+	}
+
+	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;
 }
 
diff --git a/res/res_stasis_test.c b/res/res_stasis_test.c
index 1b91b1a..9860b0e 100644
--- a/res/res_stasis_test.c
+++ b/res/res_stasis_test.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420124 $");
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
 
 #include "asterisk/astobj2.h"
 #include "asterisk/module.h"
diff --git a/res/res_statsd.c b/res/res_statsd.c
index 58e2455..4eb5260 100644
--- a/res/res_statsd.c
+++ b/res/res_statsd.c
@@ -52,7 +52,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/config_options.h"
 #include "asterisk/module.h"
@@ -97,11 +97,11 @@ static void conf_server(const struct conf *cfg, struct ast_sockaddr *addr)
 	}
 }
 
-void AST_OPTIONAL_API_NAME(ast_statsd_log_full)(const char *metric_name,
-	const char *metric_type, intmax_t value, double sample_rate)
+void AST_OPTIONAL_API_NAME(ast_statsd_log_string)(const char *metric_name,
+	const char *metric_type, const char *value, double sample_rate)
 {
-	RAII_VAR(struct conf *, cfg, NULL, ao2_cleanup);
-	RAII_VAR(struct ast_str *, msg, NULL, ast_free);
+	struct conf *cfg;
+	struct ast_str *msg;
 	size_t len;
 	struct ast_sockaddr statsd_server;
 
@@ -109,9 +109,6 @@ void AST_OPTIONAL_API_NAME(ast_statsd_log_full)(const char *metric_name,
 		return;
 	}
 
-	cfg = ao2_global_obj_ref(confs);
-	conf_server(cfg, &statsd_server);
-
 	/* Rates <= 0.0 never get logged.
 	 * Rates >= 1.0 always get logged.
 	 * All others leave it to chance.
@@ -122,9 +119,11 @@ void AST_OPTIONAL_API_NAME(ast_statsd_log_full)(const char *metric_name,
 	}
 
 	cfg = ao2_global_obj_ref(confs);
+	conf_server(cfg, &statsd_server);
 
 	msg = ast_str_create(40);
 	if (!msg) {
+		ao2_cleanup(cfg);
 		return;
 	}
 
@@ -132,7 +131,7 @@ void AST_OPTIONAL_API_NAME(ast_statsd_log_full)(const char *metric_name,
 		ast_str_append(&msg, 0, "%s.", cfg->global->prefix);
 	}
 
-	ast_str_append(&msg, 0, "%s:%jd|%s", metric_name, value, metric_type);
+	ast_str_append(&msg, 0, "%s:%s|%s", metric_name, value, metric_type);
 
 	if (sample_rate < 1.0) {
 		ast_str_append(&msg, 0, "|@%.2f", sample_rate);
@@ -144,20 +143,87 @@ void AST_OPTIONAL_API_NAME(ast_statsd_log_full)(const char *metric_name,
 
 	len = ast_str_strlen(msg);
 
-	ast_debug(6, "send: %s\n", ast_str_buffer(msg));
+	ast_debug(6, "Sending statistic %s to StatsD server\n", ast_str_buffer(msg));
 	ast_sendto(socket_fd, ast_str_buffer(msg), len, 0, &statsd_server);
+
+	ao2_cleanup(cfg);
+	ast_free(msg);
+}
+
+void AST_OPTIONAL_API_NAME(ast_statsd_log_full)(const char *metric_name,
+	const char *metric_type, intmax_t value, double sample_rate)
+{
+	char char_value[30];
+	snprintf(char_value, sizeof(char_value), "%jd", value);
+
+	ast_statsd_log_string(metric_name, metric_type, char_value, sample_rate);
+
+}
+
+AST_THREADSTORAGE(statsd_buf);
+
+void AST_OPTIONAL_API_NAME(ast_statsd_log_string_va)(const char *metric_name,
+	const char *metric_type, const char *value, double sample_rate, ...)
+{
+	struct ast_str *buf;
+	va_list ap;
+	int res;
+
+	buf = ast_str_thread_get(&statsd_buf, 128);
+	if (!buf) {
+		return;
+	}
+
+	va_start(ap, sample_rate);
+	res = ast_str_set_va(&buf, 0, metric_name, ap);
+	va_end(ap);
+
+	if (res == AST_DYNSTR_BUILD_FAILED) {
+		return;
+	}
+
+	ast_statsd_log_string(ast_str_buffer(buf), metric_type, value, sample_rate);
+}
+
+void AST_OPTIONAL_API_NAME(ast_statsd_log_full_va)(const char *metric_name,
+	const char *metric_type, intmax_t value, double sample_rate, ...)
+{
+	struct ast_str *buf;
+	va_list ap;
+	int res;
+
+	buf = ast_str_thread_get(&statsd_buf, 128);
+	if (!buf) {
+		return;
+	}
+
+	va_start(ap, sample_rate);
+	res = ast_str_set_va(&buf, 0, metric_name, ap);
+	va_end(ap);
+
+	if (res == AST_DYNSTR_BUILD_FAILED) {
+		return;
+	}
+
+	ast_statsd_log_full(ast_str_buffer(buf), metric_type, value, sample_rate);
 }
 
 void AST_OPTIONAL_API_NAME(ast_statsd_log)(const char *metric_name,
 	const char *metric_type, intmax_t value)
 {
-	ast_statsd_log_full(metric_name, metric_type, value, 1.0);
+	char char_value[30];
+	snprintf(char_value, sizeof(char_value), "%jd", value);
+
+	ast_statsd_log_string(metric_name, metric_type, char_value, 1.0);
 }
 
 void AST_OPTIONAL_API_NAME(ast_statsd_log_sample)(const char *metric_name,
 	intmax_t value, double sample_rate)
 {
-	ast_statsd_log_full(metric_name, AST_STATSD_COUNTER, value,
+	char char_value[30];
+	snprintf(char_value, sizeof(char_value), "%jd", value);
+
+	ast_statsd_log_string(metric_name, AST_STATSD_COUNTER, char_value,
 		sample_rate);
 }
 
diff --git a/res/res_statsd.exports.in b/res/res_statsd.exports.in
index 6f02b25..d4a79c1 100644
--- a/res/res_statsd.exports.in
+++ b/res/res_statsd.exports.in
@@ -3,6 +3,7 @@
 		LINKER_SYMBOL_PREFIX*ast_statsd_log;
 		LINKER_SYMBOL_PREFIX*ast_statsd_log_full;
 		LINKER_SYMBOL_PREFIX*ast_statsd_log_sample;
+		LINKER_SYMBOL_PREFIX*ast_statsd_log_string;
 	local:
 		*;
 };
diff --git a/res/res_stun_monitor.c b/res/res_stun_monitor.c
index f168318..4cdc2fd 100644
--- a/res/res_stun_monitor.c
+++ b/res/res_stun_monitor.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420124 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/sched.h"
diff --git a/res/res_timing_dahdi.c b/res/res_timing_dahdi.c
index 3a77595..1ef5117 100644
--- a/res/res_timing_dahdi.c
+++ b/res/res_timing_dahdi.c
@@ -30,7 +30,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $");
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
 
 #include <sys/types.h>
 #include <sys/stat.h>
diff --git a/res/res_timing_kqueue.c b/res/res_timing_kqueue.c
index 3e24177..d46f7f3 100644
--- a/res/res_timing_kqueue.c
+++ b/res/res_timing_kqueue.c
@@ -73,16 +73,95 @@ static struct ast_timing_interface kqueue_timing = {
 };
 
 struct kqueue_timer {
+	intptr_t period;
 	int handle;
-	uint64_t nsecs;
-	uint64_t unacked;
+#ifndef EVFILT_USER
+	int continuous_fd;
+	unsigned int continuous_fd_valid:1;
+#endif
 	unsigned int is_continuous:1;
 };
 
+#ifdef EVFILT_USER
+#define CONTINUOUS_EVFILT_TYPE EVFILT_USER
+static int kqueue_timer_init_continuous_event(struct kqueue_timer *timer)
+{
+	return 0;
+}
+
+static int kqueue_timer_enable_continuous_event(struct kqueue_timer *timer)
+{
+	struct kevent kev[2];
+
+	EV_SET(&kev[0], (uintptr_t)timer, EVFILT_USER, EV_ADD | EV_ENABLE,
+		0, 0, NULL);
+	EV_SET(&kev[1], (uintptr_t)timer, EVFILT_USER, 0, NOTE_TRIGGER,
+		0, NULL);
+	return kevent(timer->handle, kev, 2, NULL, 0, NULL);
+}
+
+static int kqueue_timer_disable_continuous_event(struct kqueue_timer *timer)
+{
+	struct kevent kev;
+
+	EV_SET(&kev, (uintptr_t)timer, EVFILT_USER, EV_DELETE, 0, 0, NULL);
+	return kevent(timer->handle, &kev, 1, NULL, 0, NULL);
+}
+
+static void kqueue_timer_fini_continuous_event(struct kqueue_timer *timer)
+{
+}
+
+#else /* EVFILT_USER */
+
+#define CONTINUOUS_EVFILT_TYPE EVFILT_READ
+static int kqueue_timer_init_continuous_event(struct kqueue_timer *timer)
+{
+	int pipefds[2];
+	int retval;
+
+	retval = pipe(pipefds);
+	if (retval == 0) {
+		timer->continuous_fd = pipefds[0];
+		timer->continuous_fd_valid = 1;
+		close(pipefds[1]);
+	}
+	return retval;
+}
+
+static void kqueue_timer_fini_continuous_event(struct kqueue_timer *timer)
+{
+	if (timer->continuous_fd_valid) {
+		close(timer->continuous_fd);
+	}
+}
+
+static int kqueue_timer_enable_continuous_event(struct kqueue_timer *timer)
+{
+	struct kevent kev;
+
+	EV_SET(&kev, timer->continuous_fd, EVFILT_READ, EV_ADD | EV_ENABLE,
+		0, 0, NULL);
+	return kevent(timer->handle, &kev, 1, NULL, 0, NULL);
+}
+
+static int kqueue_timer_disable_continuous_event(struct kqueue_timer *timer)
+{
+	struct kevent kev;
+
+	EV_SET(&kev, timer->continuous_fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
+	return kevent(timer->handle, &kev, 1, NULL, 0, NULL);
+}
+#endif
+
 static void timer_destroy(void *obj)
 {
 	struct kqueue_timer *timer = obj;
-	close(timer->handle);
+	ast_debug(5, "[%d]: Timer Destroy\n", timer->handle);
+	kqueue_timer_fini_continuous_event(timer);
+	if (timer->handle > -1) {
+		close(timer->handle);
+	}
 }
 
 static void *kqueue_timer_open(void)
@@ -90,15 +169,24 @@ static void *kqueue_timer_open(void)
 	struct kqueue_timer *timer;
 
 	if (!(timer = ao2_alloc(sizeof(*timer), timer_destroy))) {
-		ast_log(LOG_ERROR, "Could not allocate memory for kqueue_timer structure\n");
-		return -1;
+		ast_log(LOG_ERROR, "Alloc failed for kqueue_timer structure\n");
+		return NULL;
 	}
+
 	if ((timer->handle = kqueue()) < 0) {
-		ast_log(LOG_ERROR, "Failed to create kqueue timer: %s\n", strerror(errno));
+		ast_log(LOG_ERROR, "Failed to create kqueue fd: %s\n",
+			strerror(errno));
 		ao2_ref(timer, -1);
-		return -1;
+		return NULL;
 	}
 
+	if (kqueue_timer_init_continuous_event(timer) != 0) {
+		ast_log(LOG_ERROR, "Failed to create continuous event: %s\n",
+			strerror(errno));
+		ao2_ref(timer, -1);
+		return NULL;
+	}
+	ast_debug(5, "[%d]: Create timer\n", timer->handle);
 	return timer;
 }
 
@@ -106,75 +194,151 @@ static void kqueue_timer_close(void *data)
 {
 	struct kqueue_timer *timer = data;
 
+	ast_debug(5, "[%d]: Timer Close\n", timer->handle);
 	ao2_ref(timer, -1);
 }
 
-static void kqueue_set_nsecs(struct kqueue_timer *our_timer, uint64_t nsecs)
+/*
+ * Use the highest precision available that does not overflow
+ * the datatype kevent is using for time.
+ */
+static intptr_t kqueue_scale_period(unsigned int period_ns, int *units)
 {
-	struct timespec nowait = { 0, 1 };
-#ifdef HAVE_KEVENT64
-	struct kevent64_s kev;
-
-	EV_SET64(&kev, our_timer->handle, EVFILT_TIMER, EV_ADD | EV_ENABLE, NOTE_NSECONDS,
-		nsecs, 0, 0, 0);
-	kevent64(our_timer->handle, &kev, 1, NULL, 0, 0, &nowait);
-#else
-	struct kevent kev;
-
-	EV_SET(&kev, our_timer->handle, EVFILT_TIMER, EV_ADD | EV_ENABLE,
+	uint64_t period = period_ns;
+	*units = 0;
 #ifdef NOTE_NSECONDS
-		nsecs <= 0xFFffFFff ? NOTE_NSECONDS :
-#endif
-#ifdef NOTE_USECONDS
-		NOTE_USECONDS
-#else /* Milliseconds, if no constants are defined */
-		0
-#endif
-		,
-#ifdef NOTE_NSECONDS
-		nsecs <= 0xFFffFFff ? nsecs :
-#endif
+	if (period < INTPTR_MAX) {
+		*units = NOTE_NSECONDS;
+	} else {
 #ifdef NOTE_USECONDS
-	nsecs / 1000
-#else /* Milliseconds, if nothing else is defined */
-	nsecs / 1000000
-#endif
-	, NULL);
-	kevent(our_timer->handle, &kev, 1, NULL, 0, &nowait);
+		period /= 1000;
+		if (period < INTPTR_MAX) {
+			*units = NOTE_USECONDS;
+		} else {
+			period /= 1000;
+#ifdef NOTE_MSECONDS
+			*units = NOTE_MSECONDS;
+#endif	/* NOTE_MSECONDS */
+		}
+#else	/* NOTE_USECONDS */
+		period /= 1000000;
+#ifdef NOTE_MSECONDS
+		*units = NOTE_MSECONDS;
+#endif	/* NOTE_MSECONDS */
+#endif	/* NOTE_USECONDS */
+	}
+#else	/* NOTE_NSECONDS */
+	period /= 1000000;
 #endif
+	if (period > INTPTR_MAX) {
+		period = INTPTR_MAX;
+	}
+	return period;
 }
 
 static int kqueue_timer_set_rate(void *data, unsigned int rate)
 {
+	struct kevent kev;
 	struct kqueue_timer *timer = data;
+	uint64_t period_ns;
+	int flags;
+	int units;
+	int retval;
+
+	ao2_lock(timer);
 
-	kqueue_set_nsecs(timer, (timer->nsecs = rate ? (long) (1000000000 / rate) : 0L));
+	if (rate == 0) {
+		if (timer->period == 0) {
+			ao2_unlock(timer);
+			return (0);
+		}
+		flags = EV_DELETE;
+		timer->period = 0;
+		units = 0;
+	} else  {
+		flags = EV_ADD | EV_ENABLE;
+		period_ns = (uint64_t)1000000000 / rate;
+		timer->period = kqueue_scale_period(period_ns, &units);
+	}
+	ast_debug(5, "[%d]: Set rate %u:%ju\n",
+		timer->handle, units, (uintmax_t)timer->period);
+	EV_SET(&kev, timer->handle, EVFILT_TIMER, flags, units,
+		timer->period, NULL);
+	retval = kevent(timer->handle, &kev, 1, NULL, 0, NULL);
+
+	if (retval == -1) {
+		ast_log(LOG_ERROR, "[%d]: Error queing timer: %s\n",
+			timer->handle, strerror(errno));
+	}
+
+	ao2_unlock(timer);
 
 	return 0;
 }
 
 static int kqueue_timer_ack(void *data, unsigned int quantity)
 {
+	static struct timespec ts_nowait = { 0, 0 };
 	struct kqueue_timer *timer = data;
+	struct kevent kev[2];
+	int i, retval;
+
+	ao2_lock(timer);
 
-	if (timer->unacked < quantity) {
-		ast_debug(1, "Acking more events than have expired?!!\n");
-		timer->unacked = 0;
+	retval = kevent(timer->handle, NULL, 0, kev, 2, &ts_nowait);
+	if (retval == -1) {
+		ast_log(LOG_ERROR, "[%d]: Error sampling kqueue: %s\n",
+			timer->handle, strerror(errno));
+		ao2_unlock(timer);
 		return -1;
-	} else {
-		timer->unacked -= quantity;
 	}
 
+	for (i = 0; i < retval; i++) {
+		switch (kev[i].filter) {
+		case EVFILT_TIMER:
+			if (kev[i].data > quantity) {
+				ast_log(LOG_ERROR, "[%d]: Missed %ju\n",
+					timer->handle,
+					(uintmax_t)kev[i].data - quantity);
+			}
+			break;
+		case CONTINUOUS_EVFILT_TYPE:
+			if (!timer->is_continuous) {
+				ast_log(LOG_ERROR,
+					"[%d]: Spurious user event\n",
+					timer->handle);
+			}
+			break;
+		default:
+			ast_log(LOG_ERROR, "[%d]: Spurious kevent type %d.\n",
+				timer->handle, kev[i].filter);
+		}
+	}
+
+	ao2_unlock(timer);
+
 	return 0;
 }
 
 static int kqueue_timer_enable_continuous(void *data)
 {
 	struct kqueue_timer *timer = data;
+	int retval;
+
+	ao2_lock(timer);
+
+	if (!timer->is_continuous) {
+		ast_debug(5, "[%d]: Enable Continuous\n", timer->handle);
+		retval = kqueue_timer_enable_continuous_event(timer);
+		if (retval == -1) {
+			ast_log(LOG_ERROR,
+				"[%d]: Error signaling continuous event: %s\n",
+				timer->handle, strerror(errno));
+		}
+		timer->is_continuous = 1;
+	}
 
-	kqueue_set_nsecs(timer, 1);
-	timer->is_continuous = 1;
-	timer->unacked = 0;
+	ao2_unlock(timer);
 
 	return 0;
 }
@@ -182,10 +346,22 @@ static int kqueue_timer_enable_continuous(void *data)
 static int kqueue_timer_disable_continuous(void *data)
 {
 	struct kqueue_timer *timer = data;
+	int retval;
 
-	kqueue_set_nsecs(timer, timer->nsecs);
-	timer->is_continuous = 0;
-	timer->unacked = 0;
+	ao2_lock(timer);
+
+	if (timer->is_continuous) {
+		ast_debug(5, "[%d]: Disable Continuous\n", timer->handle);
+		retval = kqueue_timer_disable_continuous_event(timer);
+		if (retval == -1) {
+			ast_log(LOG_ERROR,
+				"[%d]: Error clearing continuous event: %s\n",
+				timer->handle, strerror(errno));
+		}
+		timer->is_continuous = 0;
+	}
+
+	ao2_unlock(timer);
 
 	return 0;
 }
@@ -193,19 +369,12 @@ static int kqueue_timer_disable_continuous(void *data)
 static enum ast_timer_event kqueue_timer_get_event(void *data)
 {
 	struct kqueue_timer *timer = data;
-	enum ast_timer_event res = -1;
-	struct timespec sixty_seconds = { 60, 0 };
-	struct kevent kev;
+	enum ast_timer_event res;
 
-	/* If we have non-ACKed events, just return immediately */
-	if (timer->unacked == 0) {
-		if (kevent(timer->handle, NULL, 0, &kev, 1, &sixty_seconds) > 0) {
-			timer->unacked += kev.data;
-		}
-	}
-
-	if (timer->unacked > 0) {
-		res = timer->is_continuous ? AST_TIMING_EVENT_CONTINUOUS : AST_TIMING_EVENT_EXPIRED;
+	if (timer->is_continuous) {
+		res = AST_TIMING_EVENT_CONTINUOUS;
+	} else {
+		res = AST_TIMING_EVENT_EXPIRED;
 	}
 
 	return res;
@@ -213,8 +382,7 @@ static enum ast_timer_event kqueue_timer_get_event(void *data)
 
 static unsigned int kqueue_timer_get_max_rate(void *data)
 {
-	/* Actually, the max rate is 2^64-1 seconds, but that's not representable in a 32-bit integer. */
-	return UINT_MAX;
+	return INTPTR_MAX > UINT_MAX ? UINT_MAX : INTPTR_MAX;
 }
 
 static int kqueue_timer_fd(void *data)
@@ -250,7 +418,7 @@ AST_TEST_DEFINE(test_kqueue_timing)
 	}
 
 	do {
-		pfd.fd = ast_timer_fd(kt);
+		pfd.fd = kqueue_timer_fd(kt);
 		if (kqueue_timer_set_rate(kt, 1000)) {
 			ast_test_status_update(test, "Cannot set timer rate to 1000/s\n");
 			res = AST_TEST_FAIL;
@@ -271,13 +439,12 @@ AST_TEST_DEFINE(test_kqueue_timing)
 			res = AST_TEST_FAIL;
 			break;
 		}
-#if 0
-		if (kt->unacked == 0) {
-			ast_test_status_update(test, "Unacked events is 0, but there should be at least 1.\n");
+		if (kqueue_timer_ack(kt, 1) != 0) {
+			ast_test_status_update(test, "Acking event failed.\n");
 			res = AST_TEST_FAIL;
 			break;
 		}
-#endif
+
 		kqueue_timer_enable_continuous(kt);
 		start = ast_tvnow();
 		for (i = 0; i < 100; i++) {
@@ -291,15 +458,15 @@ AST_TEST_DEFINE(test_kqueue_timing)
 				res = AST_TEST_FAIL;
 				break;
 			}
+			if (kqueue_timer_ack(kt, 1) != 0) {
+				ast_test_status_update(test, "Acking event failed.\n");
+				res = AST_TEST_FAIL;
+				break;
+			}
+
 		}
 		diff = ast_tvdiff_us(ast_tvnow(), start);
 		ast_test_status_update(test, "diff is %llu\n", diff);
-		/*
-		if (abs(diff - kt->unacked) == 0) {
-			ast_test_status_update(test, "Unacked events should be around 1000, not %llu\n", kt->unacked);
-			res = AST_TEST_FAIL;
-		}
-		*/
 	} while (0);
 	kqueue_timer_close(kt);
 	return res;
@@ -312,8 +479,8 @@ AST_TEST_DEFINE(test_kqueue_timing)
  * 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)
diff --git a/res/res_timing_pthread.c b/res/res_timing_pthread.c
index 100b652..1e76720 100644
--- a/res/res_timing_pthread.c
+++ b/res/res_timing_pthread.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $");
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
 
 #include <stdbool.h>
 #include <math.h>
@@ -143,7 +143,7 @@ static void *pthread_timer_open(void)
 		ast_cond_signal(&timing_thread.cond);
 		ast_mutex_unlock(&timing_thread.lock);
 	}
-	ao2_link(pthread_timers, timer);
+	ao2_link_flags(pthread_timers, timer, OBJ_NOLOCK);
 	ao2_unlock(pthread_timers);
 
 	return timer;
@@ -153,6 +153,7 @@ static void pthread_timer_close(void *data)
 {
 	struct pthread_timer *timer = data;
 
+	ao2_unlink(pthread_timers, timer);
 	ao2_ref(timer, -1);
 }
 
diff --git a/res/res_timing_timerfd.c b/res/res_timing_timerfd.c
index 6d5400b..43deb6c 100644
--- a/res/res_timing_timerfd.c
+++ b/res/res_timing_timerfd.c
@@ -76,8 +76,9 @@ struct timerfd_timer {
 static void timer_destroy(void *obj)
 {
 	struct timerfd_timer *timer = obj;
-
-	close(timer->fd);
+	if (timer->fd > -1) {
+		close(timer->fd);
+	}
 }
 
 static void *timerfd_timer_open(void)
diff --git a/res/res_xmpp.c b/res/res_xmpp.c
index c9f9132..d979143 100644
--- a/res/res_xmpp.c
+++ b/res/res_xmpp.c
@@ -48,7 +48,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428687 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <ctype.h>
 #include <iksemel.h>
@@ -470,6 +470,7 @@ struct ast_xmpp_client_config {
 	int message_timeout;            /*!< Timeout for messages */
 	int priority;                   /*!< Resource priority */
 	struct ast_flags flags;         /*!< Various options that have been set */
+	struct ast_flags mod_flags;     /*!< Global options that have been modified */
 	enum ikshowtype status;         /*!< Presence status */
 	struct ast_xmpp_client *client; /*!< Pointer to the client */
 	struct ao2_container *buddies;  /*!< Configured buddies */
@@ -742,8 +743,6 @@ static void *xmpp_config_alloc(void)
 		goto error;
 	}
 
-	ast_set_flag(&cfg->global->general, XMPP_AUTOREGISTER | XMPP_AUTOACCEPT | XMPP_USETLS | XMPP_USESASL | XMPP_KEEPALIVE);
-
 	if (!(cfg->clients = ao2_container_alloc(1, xmpp_config_hash, xmpp_config_cmp))) {
 		goto error;
 	}
@@ -3569,12 +3568,12 @@ int ast_xmpp_client_disconnect(struct ast_xmpp_client *client)
 	}
 
 	if (client->mwi_sub) {
-		client->mwi_sub = stasis_unsubscribe(client->mwi_sub);
+		client->mwi_sub = stasis_unsubscribe_and_join(client->mwi_sub);
 		xmpp_pubsub_unsubscribe(client, "message_waiting");
 	}
 
 	if (client->device_state_sub) {
-		client->device_state_sub = stasis_unsubscribe(client->device_state_sub);
+		client->device_state_sub = stasis_unsubscribe_and_join(client->device_state_sub);
 		xmpp_pubsub_unsubscribe(client, "device_state");
 	}
 
@@ -3835,6 +3834,10 @@ static int xmpp_client_config_merge_buddies(void *obj, void *arg, int flags)
 static int xmpp_client_config_post_apply(void *obj, void *arg, int flags)
 {
 	struct ast_xmpp_client_config *cfg = obj;
+	RAII_VAR(struct xmpp_config *, gcfg, ao2_global_obj_ref(globals), ao2_cleanup);
+
+	/* Merge global options that have not been modified */
+	ast_copy_flags(&cfg->flags, &gcfg->global->general, ~(cfg->mod_flags.flags) & (XMPP_AUTOPRUNE | XMPP_AUTOREGISTER | XMPP_AUTOACCEPT));
 
 	/* Merge buddies as need be */
 	ao2_callback(cfg->buddies, OBJ_MULTIPLE | OBJ_UNLINK, xmpp_client_config_merge_buddies, cfg->client->buddies);
@@ -4511,10 +4514,13 @@ static int client_bitfield_handler(const struct aco_option *opt, struct ast_vari
 		ast_set2_flag(&cfg->flags, ast_true(var->value), XMPP_KEEPALIVE);
 	} else if (!strcasecmp(var->name, "autoprune")) {
 		ast_set2_flag(&cfg->flags, ast_true(var->value), XMPP_AUTOPRUNE);
+		ast_set2_flag(&cfg->mod_flags, 1, XMPP_AUTOPRUNE);
 	} else if (!strcasecmp(var->name, "autoregister")) {
 		ast_set2_flag(&cfg->flags, ast_true(var->value), XMPP_AUTOREGISTER);
+		ast_set2_flag(&cfg->mod_flags, 1, XMPP_AUTOREGISTER);
 	} else if (!strcasecmp(var->name, "auth_policy")) {
 		ast_set2_flag(&cfg->flags, !strcasecmp(var->value, "accept") ? 1 : 0, XMPP_AUTOACCEPT);
+		ast_set2_flag(&cfg->mod_flags, 1, XMPP_AUTOACCEPT);
 	} else if (!strcasecmp(var->name, "sendtodialplan")) {
 		ast_set2_flag(&cfg->flags, ast_true(var->value), XMPP_SEND_TO_DIALPLAN);
 	} else {
@@ -4605,6 +4611,11 @@ static int load_module(void)
 	aco_option_register(&cfg_info, "port", ACO_EXACT, client_options, "5222", OPT_UINT_T, 0, FLDSET(struct ast_xmpp_client_config, port));
 	aco_option_register(&cfg_info, "timeout", ACO_EXACT, client_options, "5", OPT_UINT_T, 0, FLDSET(struct ast_xmpp_client_config, message_timeout));
 
+	/* Global options that can be overridden per client must not specify a default */
+	aco_option_register_custom(&cfg_info, "autoprune", ACO_EXACT, client_options, NULL, client_bitfield_handler, 0);
+	aco_option_register_custom(&cfg_info, "autoregister", ACO_EXACT, client_options, NULL, client_bitfield_handler, 0);
+	aco_option_register_custom(&cfg_info, "auth_policy", ACO_EXACT, client_options, NULL, client_bitfield_handler, 0);
+
 	aco_option_register_custom(&cfg_info, "debug", ACO_EXACT, client_options, "no", client_bitfield_handler, 0);
 	aco_option_register_custom(&cfg_info, "type", ACO_EXACT, client_options, "client", client_bitfield_handler, 0);
 	aco_option_register_custom(&cfg_info, "distribute_events", ACO_EXACT, client_options, "no", client_bitfield_handler, 0);
@@ -4612,9 +4623,6 @@ static int load_module(void)
 	aco_option_register_custom(&cfg_info, "usesasl", ACO_EXACT, client_options, "yes", client_bitfield_handler, 0);
 	aco_option_register_custom(&cfg_info, "forceoldssl", ACO_EXACT, client_options, "no", client_bitfield_handler, 0);
 	aco_option_register_custom(&cfg_info, "keepalive", ACO_EXACT, client_options, "yes", client_bitfield_handler, 0);
-	aco_option_register_custom(&cfg_info, "autoprune", ACO_EXACT, client_options, "no", client_bitfield_handler, 0);
-	aco_option_register_custom(&cfg_info, "autoregister", ACO_EXACT, client_options, "yes", client_bitfield_handler, 0);
-	aco_option_register_custom(&cfg_info, "auth_policy", ACO_EXACT, client_options, "accept", client_bitfield_handler, 0);
 	aco_option_register_custom(&cfg_info, "sendtodialplan", ACO_EXACT, client_options, "no", client_bitfield_handler, 0);
 	aco_option_register_custom(&cfg_info, "status", ACO_EXACT, client_options, "available", client_status_handler, 0);
 	aco_option_register_custom(&cfg_info, "buddy", ACO_EXACT, client_options, NULL, client_buddy_handler, 0);
diff --git a/res/snmp/agent.c b/res/snmp/agent.c
index b607ecf..9d1528d 100644
--- a/res/snmp/agent.c
+++ b/res/snmp/agent.c
@@ -20,7 +20,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 /*
  * There is some collision collision between netsmp and asterisk names,
@@ -298,12 +298,18 @@ static u_char *ast_var_channels_table(struct variable *vp, oid *name, size_t *le
 		}
 		break;
 	case ASTCHANBRIDGE:
-		if ((bridge = ast_channel_bridge_peer(chan)) != NULL) {
+		ast_channel_unlock(chan);
+		bridge = ast_channel_bridge_peer(chan);
+		if (bridge) {
+			ast_channel_lock(bridge);
 			ast_copy_string(string_ret, ast_channel_name(bridge), sizeof(string_ret));
+			ast_channel_unlock(bridge);
+			ast_channel_unref(bridge);
+
 			*var_len = strlen(string_ret);
 			ret = (u_char *)string_ret;
-			ast_channel_unref(bridge);
 		}
+		ast_channel_lock(chan);
 		break;
 	case ASTCHANMASQ:
 		if (ast_channel_masq(chan) && !ast_strlen_zero(ast_channel_name(ast_channel_masq(chan)))) {
diff --git a/res/stasis/app.c b/res/stasis/app.c
index a7525d7..4e18aa5 100644
--- a/res/stasis/app.c
+++ b/res/stasis/app.c
@@ -25,7 +25,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 429064 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "app.h"
 #include "control.h"
@@ -38,6 +38,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 429064 $")
 #include "asterisk/stasis_endpoints.h"
 #include "asterisk/stasis_message_router.h"
 
+#define BRIDGE_ALL "__AST_BRIDGE_ALL_TOPIC"
+#define CHANNEL_ALL "__AST_CHANNEL_ALL_TOPIC"
+#define ENDPOINT_ALL "__AST_ENDPOINT_ALL_TOPIC"
+
 static int unsubscribe(struct stasis_app *app, const char *kind, const char *id, int terminate);
 
 struct stasis_app {
@@ -47,12 +51,16 @@ struct stasis_app {
 	struct stasis_message_router *router;
 	/*! Router for handling messages to the bridge all \a topic. */
 	struct stasis_message_router *bridge_router;
+	/*! Optional router for handling endpoint messages in 'all' subscriptions */
+	struct stasis_message_router *endpoint_router;
 	/*! Container of the channel forwards to this app's topic. */
 	struct ao2_container *forwards;
 	/*! Callback function for this application. */
 	stasis_app_cb handler;
 	/*! Opaque data to hand to callback function. */
 	void *data;
+	/*! Subscription model for the application */
+	enum stasis_app_subscription_model subscription_model;
 	/*! Name of the Stasis application */
 	char name[];
 };
@@ -121,34 +129,33 @@ static struct app_forwards *forwards_create(struct stasis_app *app,
 static struct app_forwards *forwards_create_channel(struct stasis_app *app,
 	struct ast_channel *chan)
 {
-	RAII_VAR(struct app_forwards *, forwards, NULL, ao2_cleanup);
+	struct app_forwards *forwards;
 
-	if (!app || !chan) {
+	if (!app) {
 		return NULL;
 	}
 
-	forwards = forwards_create(app, ast_channel_uniqueid(chan));
+	forwards = forwards_create(app, chan ? ast_channel_uniqueid(chan) : CHANNEL_ALL);
 	if (!forwards) {
 		return NULL;
 	}
 
 	forwards->forward_type = FORWARD_CHANNEL;
-	forwards->topic_forward = stasis_forward_all(ast_channel_topic(chan),
-		app->topic);
-	if (!forwards->topic_forward) {
-		return NULL;
+	if (chan) {
+		forwards->topic_forward = stasis_forward_all(ast_channel_topic(chan),
+			app->topic);
 	}
-
 	forwards->topic_cached_forward = stasis_forward_all(
-		ast_channel_topic_cached(chan), app->topic);
-	if (!forwards->topic_cached_forward) {
+		chan ? ast_channel_topic_cached(chan) : ast_channel_topic_all_cached(),
+		app->topic);
+
+	if ((!forwards->topic_forward && chan) || !forwards->topic_cached_forward) {
 		/* Half-subscribed is a bad thing */
-		stasis_forward_cancel(forwards->topic_forward);
-		forwards->topic_forward = NULL;
+		forwards_unsubscribe(forwards);
+		ao2_ref(forwards, -1);
 		return NULL;
 	}
 
-	ao2_ref(forwards, +1);
 	return forwards;
 }
 
@@ -156,69 +163,101 @@ static struct app_forwards *forwards_create_channel(struct stasis_app *app,
 static struct app_forwards *forwards_create_bridge(struct stasis_app *app,
 	struct ast_bridge *bridge)
 {
-	RAII_VAR(struct app_forwards *, forwards, NULL, ao2_cleanup);
+	struct app_forwards *forwards;
 
-	if (!app || !bridge) {
+	if (!app) {
 		return NULL;
 	}
 
-	forwards = forwards_create(app, bridge->uniqueid);
+	forwards = forwards_create(app, bridge ? bridge->uniqueid : BRIDGE_ALL);
 	if (!forwards) {
 		return NULL;
 	}
 
 	forwards->forward_type = FORWARD_BRIDGE;
-	forwards->topic_forward = stasis_forward_all(ast_bridge_topic(bridge),
-		app->topic);
-	if (!forwards->topic_forward) {
-		return NULL;
+	if (bridge) {
+		forwards->topic_forward = stasis_forward_all(ast_bridge_topic(bridge),
+			app->topic);
 	}
-
 	forwards->topic_cached_forward = stasis_forward_all(
-		ast_bridge_topic_cached(bridge), app->topic);
-	if (!forwards->topic_cached_forward) {
+		bridge ? ast_bridge_topic_cached(bridge) : ast_bridge_topic_all_cached(),
+		app->topic);
+
+	if ((!forwards->topic_forward && bridge) || !forwards->topic_cached_forward) {
 		/* Half-subscribed is a bad thing */
-		stasis_forward_cancel(forwards->topic_forward);
-		forwards->topic_forward = NULL;
+		forwards_unsubscribe(forwards);
+		ao2_ref(forwards, -1);
 		return NULL;
 	}
 
-	ao2_ref(forwards, +1);
 	return forwards;
 }
 
+static void endpoint_state_cb(void *data, struct stasis_subscription *sub,
+	struct stasis_message *message)
+{
+	struct stasis_app *app = data;
+
+	stasis_publish(app->topic, message);
+}
+
 /*! Forward a endpoint's topics to an app */
 static struct app_forwards *forwards_create_endpoint(struct stasis_app *app,
 	struct ast_endpoint *endpoint)
 {
-	RAII_VAR(struct app_forwards *, forwards, NULL, ao2_cleanup);
 
-	if (!app || !endpoint) {
+	struct app_forwards *forwards;
+	int ret = 0;
+
+	if (!app) {
 		return NULL;
 	}
 
-	forwards = forwards_create(app, ast_endpoint_get_id(endpoint));
+	forwards = forwards_create(app, endpoint ? ast_endpoint_get_id(endpoint) : ENDPOINT_ALL);
 	if (!forwards) {
 		return NULL;
 	}
 
 	forwards->forward_type = FORWARD_ENDPOINT;
-	forwards->topic_forward = stasis_forward_all(ast_endpoint_topic(endpoint),
-		app->topic);
-	if (!forwards->topic_forward) {
-		return NULL;
-	}
+	if (endpoint) {
+		forwards->topic_forward = stasis_forward_all(ast_endpoint_topic(endpoint),
+			app->topic);
+		forwards->topic_cached_forward = stasis_forward_all(
+			ast_endpoint_topic_cached(endpoint), app->topic);
+
+		if (!forwards->topic_forward || !forwards->topic_cached_forward) {
+			/* Half-subscribed is a bad thing */
+			forwards_unsubscribe(forwards);
+			ao2_ref(forwards, -1);
+			return NULL;
+		}
+	} else {
+		/* Since endpoint subscriptions also subscribe to channels, in the case
+		 * of all endpoint subscriptions, we only want messages for the endpoints.
+		 * As such, we route those particular messages and then re-publish them
+		 * on the app's topic.
+		 */
+		ast_assert(app->endpoint_router == NULL);
+		app->endpoint_router = stasis_message_router_create(ast_endpoint_topic_all_cached());
+		if (!app->endpoint_router) {
+			forwards_unsubscribe(forwards);
+			ao2_ref(forwards, -1);
+			return NULL;
+		}
 
-	forwards->topic_cached_forward = stasis_forward_all(
-		ast_endpoint_topic_cached(endpoint), app->topic);
-	if (!forwards->topic_cached_forward) {
-		/* Half-subscribed is a bad thing */
-		stasis_forward_cancel(forwards->topic_forward);
-		forwards->topic_forward = NULL;
-		return NULL;
+		ret |= stasis_message_router_add(app->endpoint_router,
+			ast_endpoint_state_type(), endpoint_state_cb, app);
+		ret |= stasis_message_router_add(app->endpoint_router,
+			ast_endpoint_contact_state_type(), endpoint_state_cb, app);
+
+		if (ret) {
+			ao2_ref(app->endpoint_router, -1);
+			app->endpoint_router = NULL;
+			ao2_ref(forwards, -1);
+			return NULL;
+		}
 	}
 
-	ao2_ref(forwards, +1);
 	return forwards;
 }
 
@@ -260,6 +299,7 @@ static void app_dtor(void *obj)
 
 	ast_assert(app->router == NULL);
 	ast_assert(app->bridge_router == NULL);
+	ast_assert(app->endpoint_router == NULL);
 
 	ao2_cleanup(app->topic);
 	app->topic = NULL;
@@ -570,11 +610,11 @@ static int message_received_handler(const char *endpoint_id, struct ast_json *js
 		return -1;
 	}
 
-	app_send(app, ast_json_pack("{s: s, s: o, s: o, s: O}",
+	app_send(app, ast_json_pack("{s: s, s: o, s: o, s: o}",
 		"type", "TextMessageReceived",
 		"timestamp", ast_json_timeval(ast_tvnow(), NULL),
 		"endpoint", json_endpoint,
-		"message", json_msg));
+		"message", ast_json_ref(json_msg)));
 
 	return 0;
 }
@@ -724,23 +764,6 @@ static int bridge_app_subscribed_involved(struct stasis_app *app, struct ast_bri
 	return subscribed;
 }
 
-static void set_replacement_channel(struct ast_channel_snapshot *to_be_replaced,
-		struct ast_channel_snapshot *replacing)
-{
-	struct stasis_app_control *control = stasis_app_control_find_by_channel_id(
-		to_be_replaced->uniqueid);
-	struct ast_channel *chan = ast_channel_get_by_name(replacing->uniqueid);
-
-	if (control && chan) {
-		ast_channel_lock(chan);
-		app_set_replace_channel_app(chan, app_name(control_app(control)));
-		app_set_replace_channel_snapshot(chan, to_be_replaced);
-		ast_channel_unlock(chan);
-	}
-	ast_channel_cleanup(chan);
-	ao2_cleanup(control);
-}
-
 static void bridge_blind_transfer_handler(void *data, struct stasis_subscription *sub,
 	struct stasis_message *message)
 {
@@ -748,10 +771,6 @@ static void bridge_blind_transfer_handler(void *data, struct stasis_subscription
 	struct ast_blind_transfer_message *transfer_msg = stasis_message_data(message);
 	struct ast_bridge_snapshot *bridge = transfer_msg->bridge;
 
-	if (transfer_msg->replace_channel) {
-		set_replacement_channel(transfer_msg->transferer, transfer_msg->replace_channel);
-	}
-
 	if (bridge_app_subscribed(app, transfer_msg->transferer->uniqueid) ||
 		(bridge && bridge_app_subscribed_involved(app, bridge))) {
 		stasis_publish(app->topic, message);
@@ -802,18 +821,6 @@ static void bridge_attended_transfer_handler(void *data, struct stasis_subscript
 	if (subscribed) {
 		stasis_publish(app->topic, message);
 	}
-
-	if (transfer_msg->replace_channel) {
-		set_replacement_channel(transfer_msg->to_transferee.channel_snapshot,
-				transfer_msg->replace_channel);
-	}
-
-	if (transfer_msg->dest_type == AST_ATTENDED_TRANSFER_DEST_LINK) {
-		set_replacement_channel(transfer_msg->to_transferee.channel_snapshot,
-				transfer_msg->dest.links[0]);
-		set_replacement_channel(transfer_msg->to_transfer_target.channel_snapshot,
-				transfer_msg->dest.links[1]);
-	}
 }
 
 static void bridge_default_handler(void *data, struct stasis_subscription *sub,
@@ -826,7 +833,7 @@ static void bridge_default_handler(void *data, struct stasis_subscription *sub,
 	}
 }
 
-struct stasis_app *app_create(const char *name, stasis_app_cb handler, void *data)
+struct stasis_app *app_create(const char *name, stasis_app_cb handler, void *data, enum stasis_app_subscription_model subscription_model)
 {
 	RAII_VAR(struct stasis_app *, app, NULL, ao2_cleanup);
 	size_t size;
@@ -839,10 +846,10 @@ struct stasis_app *app_create(const char *name, stasis_app_cb handler, void *dat
 
 	size = sizeof(*app) + strlen(name) + 1;
 	app = ao2_alloc_options(size, app_dtor, AO2_ALLOC_OPT_LOCK_MUTEX);
-
 	if (!app) {
 		return NULL;
 	}
+	app->subscription_model = subscription_model;
 
 	app->forwards = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_MUTEX,
 		AO2_CONTAINER_ALLOC_OPT_DUPS_OBJ_REJECT,
@@ -904,14 +911,14 @@ struct stasis_app *app_create(const char *name, stasis_app_cb handler, void *dat
 
 	strncpy(app->name, name, size - sizeof(*app));
 	app->handler = handler;
-	ao2_ref(data, +1);
-	app->data = data;
+	app->data = ao2_bump(data);
 
 	ao2_ref(app, +1);
 	return app;
 }
 
-struct stasis_topic *ast_app_get_topic(struct stasis_app *app) {
+struct stasis_topic *ast_app_get_topic(struct stasis_app *app)
+{
 	return app->topic;
 }
 
@@ -964,6 +971,8 @@ void app_shutdown(struct stasis_app *app)
 	app->router = NULL;
 	stasis_message_router_unsubscribe(app->bridge_router);
 	app->bridge_router = NULL;
+	stasis_message_router_unsubscribe(app->endpoint_router);
+	app->endpoint_router = NULL;
 }
 
 int app_is_active(struct stasis_app *app)
@@ -983,7 +992,7 @@ void app_update(struct stasis_app *app, stasis_app_cb handler, void *data)
 {
 	SCOPED_AO2LOCK(lock, app);
 
-	if (app->handler) {
+	if (app->handler && app->data) {
 		RAII_VAR(struct ast_json *, msg, NULL, ast_json_unref);
 
 		ast_verb(1, "Replacing Stasis app '%s'\n", app->name);
@@ -1063,34 +1072,47 @@ struct ast_json *app_to_json(const struct stasis_app *app)
 
 int app_subscribe_channel(struct stasis_app *app, struct ast_channel *chan)
 {
+	struct app_forwards *forwards;
+	SCOPED_AO2LOCK(lock, app->forwards);
 	int res;
 
-	if (!app || !chan) {
+	if (!app) {
 		return -1;
-	} else {
-		RAII_VAR(struct app_forwards *, forwards, NULL, ao2_cleanup);
-		SCOPED_AO2LOCK(lock, app->forwards);
+	}
 
-		forwards = ao2_find(app->forwards, ast_channel_uniqueid(chan),
-			OBJ_SEARCH_KEY | OBJ_NOLOCK);
-		if (!forwards) {
-			/* Forwards not found, create one */
-			forwards = forwards_create_channel(app, chan);
-			if (!forwards) {
-				return -1;
-			}
+	/* If subscribed to all, don't subscribe again */
+	forwards = ao2_find(app->forwards, CHANNEL_ALL, OBJ_SEARCH_KEY | OBJ_NOLOCK);
+	if (forwards) {
+		ao2_ref(forwards, -1);
+		return 0;
+	}
 
-			res = ao2_link_flags(app->forwards, forwards,
-				OBJ_NOLOCK);
-			if (!res) {
-				return -1;
-			}
+	forwards = ao2_find(app->forwards,
+		chan ? ast_channel_uniqueid(chan) : CHANNEL_ALL,
+		OBJ_SEARCH_KEY | OBJ_NOLOCK);
+	if (!forwards) {
+		/* Forwards not found, create one */
+		forwards = forwards_create_channel(app, chan);
+		if (!forwards) {
+			return -1;
 		}
 
-		++forwards->interested;
-		ast_debug(3, "Channel '%s' is %d interested in %s\n", ast_channel_uniqueid(chan), forwards->interested, app->name);
-		return 0;
+		res = ao2_link_flags(app->forwards, forwards,
+			OBJ_NOLOCK);
+		if (!res) {
+			ao2_ref(forwards, -1);
+			return -1;
+		}
 	}
+
+	++forwards->interested;
+	ast_debug(3, "Channel '%s' is %d interested in %s\n",
+		chan ? ast_channel_uniqueid(chan) : "ALL",
+		forwards->interested,
+		app->name);
+
+	ao2_ref(forwards, -1);
+	return 0;
 }
 
 static int subscribe_channel(struct stasis_app *app, void *obj)
@@ -1103,6 +1125,19 @@ static int unsubscribe(struct stasis_app *app, const char *kind, const char *id,
 	RAII_VAR(struct app_forwards *, forwards, NULL, ao2_cleanup);
 	SCOPED_AO2LOCK(lock, app->forwards);
 
+	if (!id) {
+		if (!strcmp(kind, "bridge")) {
+			id = BRIDGE_ALL;
+		} else if (!strcmp(kind, "channel")) {
+			id = CHANNEL_ALL;
+		} else if (!strcmp(kind, "endpoint")) {
+			id = ENDPOINT_ALL;
+		} else {
+			ast_log(LOG_WARNING, "Unknown subscription kind '%s'\n", kind);
+			return -1;
+		}
+	}
+
 	forwards = ao2_find(app->forwards, id, OBJ_SEARCH_KEY | OBJ_NOLOCK);
 	if (!forwards) {
 		ast_debug(3, "App '%s' not subscribed to %s '%s'\n", app->name, kind, id);
@@ -1129,16 +1164,16 @@ static int unsubscribe(struct stasis_app *app, const char *kind, const char *id,
 
 int app_unsubscribe_channel(struct stasis_app *app, struct ast_channel *chan)
 {
-	if (!app || !chan) {
+	if (!app) {
 		return -1;
 	}
 
-	return app_unsubscribe_channel_id(app, ast_channel_uniqueid(chan));
+	return app_unsubscribe_channel_id(app, chan ? ast_channel_uniqueid(chan) : CHANNEL_ALL);
 }
 
 int app_unsubscribe_channel_id(struct stasis_app *app, const char *channel_id)
 {
-	if (!app || !channel_id) {
+	if (!app) {
 		return -1;
 	}
 
@@ -1148,6 +1183,10 @@ int app_unsubscribe_channel_id(struct stasis_app *app, const char *channel_id)
 int app_is_subscribed_channel_id(struct stasis_app *app, const char *channel_id)
 {
 	RAII_VAR(struct app_forwards *, forwards, NULL, ao2_cleanup);
+
+	if (ast_strlen_zero(channel_id)) {
+		channel_id = CHANNEL_ALL;
+	}
 	forwards = ao2_find(app->forwards, channel_id, OBJ_SEARCH_KEY);
 	return forwards != NULL;
 }
@@ -1167,28 +1206,39 @@ struct stasis_app_event_source channel_event_source = {
 
 int app_subscribe_bridge(struct stasis_app *app, struct ast_bridge *bridge)
 {
-	if (!app || !bridge) {
+	struct app_forwards *forwards;
+	SCOPED_AO2LOCK(lock, app->forwards);
+
+	if (!app) {
 		return -1;
-	} else {
-		RAII_VAR(struct app_forwards *, forwards, NULL, ao2_cleanup);
-		SCOPED_AO2LOCK(lock, app->forwards);
+	}
 
-		forwards = ao2_find(app->forwards, bridge->uniqueid,
-			OBJ_SEARCH_KEY | OBJ_NOLOCK);
+	/* If subscribed to all, don't subscribe again */
+	forwards = ao2_find(app->forwards, BRIDGE_ALL, OBJ_SEARCH_KEY | OBJ_NOLOCK);
+	if (forwards) {
+		ao2_ref(forwards, -1);
+		return 0;
+	}
 
+	forwards = ao2_find(app->forwards, bridge ? bridge->uniqueid : BRIDGE_ALL,
+		OBJ_SEARCH_KEY | OBJ_NOLOCK);
+	if (!forwards) {
+		/* Forwards not found, create one */
+		forwards = forwards_create_bridge(app, bridge);
 		if (!forwards) {
-			/* Forwards not found, create one */
-			forwards = forwards_create_bridge(app, bridge);
-			if (!forwards) {
-				return -1;
-			}
-			ao2_link_flags(app->forwards, forwards, OBJ_NOLOCK);
+			return -1;
 		}
-
-		++forwards->interested;
-		ast_debug(3, "Bridge '%s' is %d interested in %s\n", bridge->uniqueid, forwards->interested, app->name);
-		return 0;
+		ao2_link_flags(app->forwards, forwards, OBJ_NOLOCK);
 	}
+
+	++forwards->interested;
+	ast_debug(3, "Bridge '%s' is %d interested in %s\n",
+		bridge ? bridge->uniqueid : "ALL",
+		forwards->interested,
+		app->name);
+
+	ao2_ref(forwards, -1);
+	return 0;
 }
 
 static int subscribe_bridge(struct stasis_app *app, void *obj)
@@ -1198,16 +1248,16 @@ static int subscribe_bridge(struct stasis_app *app, void *obj)
 
 int app_unsubscribe_bridge(struct stasis_app *app, struct ast_bridge *bridge)
 {
-	if (!app || !bridge) {
+	if (!app) {
 		return -1;
 	}
 
-	return app_unsubscribe_bridge_id(app, bridge->uniqueid);
+	return app_unsubscribe_bridge_id(app, bridge ? bridge->uniqueid : BRIDGE_ALL);
 }
 
 int app_unsubscribe_bridge_id(struct stasis_app *app, const char *bridge_id)
 {
-	if (!app || !bridge_id) {
+	if (!app) {
 		return -1;
 	}
 
@@ -1216,9 +1266,26 @@ int app_unsubscribe_bridge_id(struct stasis_app *app, const char *bridge_id)
 
 int app_is_subscribed_bridge_id(struct stasis_app *app, const char *bridge_id)
 {
-	RAII_VAR(struct app_forwards *, forwards, NULL, ao2_cleanup);
-	forwards = ao2_find(app->forwards, bridge_id, OBJ_SEARCH_KEY);
-	return forwards != NULL;
+	struct app_forwards *forwards;
+	SCOPED_AO2LOCK(lock, app->forwards);
+
+	forwards = ao2_find(app->forwards, BRIDGE_ALL, OBJ_SEARCH_KEY | OBJ_NOLOCK);
+	if (forwards) {
+		ao2_ref(forwards, -1);
+		return 1;
+	}
+
+	if (ast_strlen_zero(bridge_id)) {
+		bridge_id = BRIDGE_ALL;
+	}
+
+	forwards = ao2_find(app->forwards, bridge_id, OBJ_SEARCH_KEY | OBJ_NOLOCK);
+	if (forwards) {
+		ao2_ref(forwards, -1);
+		return 1;
+	}
+
+	return 0;
 }
 
 static void *bridge_find(const struct stasis_app *app, const char *id)
@@ -1236,31 +1303,43 @@ struct stasis_app_event_source bridge_event_source = {
 
 int app_subscribe_endpoint(struct stasis_app *app, struct ast_endpoint *endpoint)
 {
-	if (!app || !endpoint) {
+	struct app_forwards *forwards;
+	SCOPED_AO2LOCK(lock, app->forwards);
+
+	if (!app) {
 		return -1;
-	} else {
-		RAII_VAR(struct app_forwards *, forwards, NULL, ao2_cleanup);
-		SCOPED_AO2LOCK(lock, app->forwards);
+	}
 
-		forwards = ao2_find(app->forwards, ast_endpoint_get_id(endpoint),
-			OBJ_SEARCH_KEY | OBJ_NOLOCK);
+	/* If subscribed to all, don't subscribe again */
+	forwards = ao2_find(app->forwards, ENDPOINT_ALL, OBJ_SEARCH_KEY | OBJ_NOLOCK);
+	if (forwards) {
+		ao2_ref(forwards, -1);
+		return 0;
+	}
 
+	forwards = ao2_find(app->forwards,
+		endpoint ? ast_endpoint_get_id(endpoint) : ENDPOINT_ALL,
+		OBJ_SEARCH_KEY | OBJ_NOLOCK);
+	if (!forwards) {
+		/* Forwards not found, create one */
+		forwards = forwards_create_endpoint(app, endpoint);
 		if (!forwards) {
-			/* Forwards not found, create one */
-			forwards = forwards_create_endpoint(app, endpoint);
-			if (!forwards) {
-				return -1;
-			}
-			ao2_link_flags(app->forwards, forwards, OBJ_NOLOCK);
-
-			/* Subscribe for messages */
-			messaging_app_subscribe_endpoint(app->name, endpoint, &message_received_handler, app);
+			return -1;
 		}
+		ao2_link_flags(app->forwards, forwards, OBJ_NOLOCK);
 
-		++forwards->interested;
-		ast_debug(3, "Endpoint '%s' is %d interested in %s\n", ast_endpoint_get_id(endpoint), forwards->interested, app->name);
-		return 0;
+		/* Subscribe for messages */
+		messaging_app_subscribe_endpoint(app->name, endpoint, &message_received_handler, app);
 	}
+
+	++forwards->interested;
+	ast_debug(3, "Endpoint '%s' is %d interested in %s\n",
+		endpoint ? ast_endpoint_get_id(endpoint) : "ALL",
+		forwards->interested,
+		app->name);
+
+	ao2_ref(forwards, -1);
+	return 0;
 }
 
 static int subscribe_endpoint(struct stasis_app *app, void *obj)
@@ -1270,7 +1349,7 @@ static int subscribe_endpoint(struct stasis_app *app, void *obj)
 
 int app_unsubscribe_endpoint_id(struct stasis_app *app, const char *endpoint_id)
 {
-	if (!app || !endpoint_id) {
+	if (!app) {
 		return -1;
 	}
 
@@ -1280,6 +1359,10 @@ int app_unsubscribe_endpoint_id(struct stasis_app *app, const char *endpoint_id)
 int app_is_subscribed_endpoint_id(struct stasis_app *app, const char *endpoint_id)
 {
 	RAII_VAR(struct app_forwards *, forwards, NULL, ao2_cleanup);
+
+	if (ast_strlen_zero(endpoint_id)) {
+		endpoint_id = ENDPOINT_ALL;
+	}
 	forwards = ao2_find(app->forwards, endpoint_id, OBJ_SEARCH_KEY);
 	return forwards != NULL;
 }
diff --git a/res/stasis/app.h b/res/stasis/app.h
index 59574f5..2c8db1c 100644
--- a/res/stasis/app.h
+++ b/res/stasis/app.h
@@ -36,6 +36,19 @@
  */
 struct stasis_app;
 
+enum stasis_app_subscription_model {
+	/*
+	 * \brief An application must manually subscribe to each
+	 * resource that it cares about. This is the default approach.
+	 */
+	STASIS_APP_SUBSCRIBE_MANUAL,
+	/*
+	 * \brief An application is automatically subscribed to all
+	 * resources in Asterisk, even if it does not control them.
+	 */
+	STASIS_APP_SUBSCRIBE_ALL
+};
+
 /*!
  * \brief Create a res_stasis application.
  *
@@ -45,7 +58,7 @@ struct stasis_app;
  * \return New \c res_stasis application.
  * \return \c NULL on error.
  */
-struct stasis_app *app_create(const char *name, stasis_app_cb handler, void *data);
+struct stasis_app *app_create(const char *name, stasis_app_cb handler, void *data, enum stasis_app_subscription_model subscription_model);
 
 /*!
  * \brief Tears down an application.
diff --git a/res/stasis/command.c b/res/stasis/command.c
index f3bdb9c..534e434 100644
--- a/res/stasis/command.c
+++ b/res/stasis/command.c
@@ -25,7 +25,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 421880 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "command.h"
 
diff --git a/res/stasis/control.c b/res/stasis/control.c
index 0e09d1c..87362df 100644
--- a/res/stasis/control.c
+++ b/res/stasis/control.c
@@ -25,7 +25,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 421880 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/stasis_channels.h"
 
@@ -428,6 +428,38 @@ int stasis_app_control_continue(struct stasis_app_control *control, const char *
 	return 0;
 }
 
+static int app_control_redirect(struct stasis_app_control *control,
+	struct ast_channel *chan, void *data)
+{
+	char *endpoint = data;
+	int res;
+
+	ast_assert(control->channel != NULL);
+	ast_assert(endpoint != NULL);
+
+	res = ast_transfer(control->channel, endpoint);
+	if (!res) {
+		ast_log(LOG_NOTICE, "Unsupported transfer requested on channel '%s'\n",
+			ast_channel_name(control->channel));
+		return 0;
+	}
+
+	return 0;
+}
+
+int stasis_app_control_redirect(struct stasis_app_control *control, const char *endpoint)
+{
+	char *endpoint_data = ast_strdup(endpoint);
+
+	if (!endpoint_data) {
+		return -1;
+	}
+
+	stasis_app_send_command_async(control, app_control_redirect, endpoint_data, ast_free_ptr);
+
+	return 0;
+}
+
 struct stasis_app_control_dtmf_data {
 	int before;
 	int between;
@@ -713,7 +745,7 @@ static int app_send_command_on_condition(struct stasis_app_control *control,
 {
 	RAII_VAR(struct stasis_app_command *, command, NULL, ao2_cleanup);
 
-	if (control == NULL) {
+	if (control == NULL || control->is_done) {
 		return -1;
 	}
 
@@ -738,7 +770,7 @@ int stasis_app_send_command_async(struct stasis_app_control *control,
 {
 	RAII_VAR(struct stasis_app_command *, command, NULL, ao2_cleanup);
 
-	if (control == NULL) {
+	if (control == NULL || control->is_done) {
 		return -1;
 	}
 
diff --git a/res/stasis/messaging.c b/res/stasis/messaging.c
index fcc17e7..229a3a6 100644
--- a/res/stasis/messaging.c
+++ b/res/stasis/messaging.c
@@ -26,7 +26,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420100 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/message.h"
 #include "asterisk/endpoints.h"
@@ -38,6 +38,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420100 $")
 #include "messaging.h"
 
 /*!
+ * \brief Subscription to all technologies
+ */
+#define TECH_WILDCARD "__AST_ALL_TECH"
+
+/*!
  * \brief Number of buckets for the \ref endpoint_subscriptions container
  */
 #define ENDPOINTS_NUM_BUCKETS 127
@@ -219,10 +224,14 @@ static int has_destination_cb(const struct ast_msg *msg)
 	for (i = 0; i < AST_VECTOR_SIZE(&tech_subscriptions); i++) {
 		sub = AST_VECTOR_GET(&tech_subscriptions, i);
 
-		if (sub && (!strncasecmp(sub->token, buf, strlen(sub->token))
-		            || !strncasecmp(sub->token, buf, strlen(sub->token)))) {
+		if (!sub) {
+			continue;
+		}
+
+		if (!strcmp(sub->token, TECH_WILDCARD)
+		    || !strncasecmp(sub->token, buf, strlen(sub->token))
+		    || !strncasecmp(sub->token, buf, strlen(sub->token))) {
 			ast_rwlock_unlock(&tech_subscriptions_lock);
-			sub = NULL; /* No ref bump! */
 			goto match;
 		}
 
@@ -231,6 +240,7 @@ static int has_destination_cb(const struct ast_msg *msg)
 
 	sub = ao2_find(endpoint_subscriptions, buf, OBJ_SEARCH_KEY);
 	if (sub) {
+		ao2_ref(sub, -1);
 		goto match;
 	}
 
@@ -238,7 +248,6 @@ static int has_destination_cb(const struct ast_msg *msg)
 	return 0;
 
 match:
-	ao2_cleanup(sub);
 	return 1;
 }
 
@@ -301,7 +310,8 @@ static int handle_msg_cb(struct ast_msg *msg)
 			continue;
 		}
 
-		if (!strncasecmp(sub->token, buf, strlen(sub->token))) {
+		if (!strcmp(sub->token, TECH_WILDCARD)
+		    || !strncasecmp(sub->token, buf, strlen(sub->token))) {
 			ast_rwlock_unlock(&tech_subscriptions_lock);
 			ao2_bump(sub);
 			endpoint_name = buf;
@@ -374,7 +384,7 @@ static struct message_subscription *get_subscription(struct ast_endpoint *endpoi
 {
 	struct message_subscription *sub = NULL;
 
-	if (!ast_strlen_zero(ast_endpoint_get_resource(endpoint))) {
+	if (endpoint && !ast_strlen_zero(ast_endpoint_get_resource(endpoint))) {
 		sub = ao2_find(endpoint_subscriptions, endpoint, OBJ_SEARCH_KEY);
 	} else {
 		int i;
@@ -383,7 +393,7 @@ static struct message_subscription *get_subscription(struct ast_endpoint *endpoi
 		for (i = 0; i < AST_VECTOR_SIZE(&tech_subscriptions); i++) {
 			sub = AST_VECTOR_GET(&tech_subscriptions, i);
 
-			if (sub && !strcmp(sub->token, ast_endpoint_get_tech(endpoint))) {
+			if (sub && !strcmp(sub->token, endpoint ? ast_endpoint_get_tech(endpoint) : TECH_WILDCARD)) {
 				ao2_bump(sub);
 				break;
 			}
@@ -400,10 +410,6 @@ void messaging_app_unsubscribe_endpoint(const char *app_name, const char *endpoi
 	RAII_VAR(struct ast_endpoint *, endpoint, NULL, ao2_cleanup);
 
 	endpoint = ast_endpoint_find_by_id(endpoint_id);
-	if (!endpoint) {
-		return;
-	}
-
 	sub = get_subscription(endpoint);
 	if (!sub) {
 		return;
@@ -417,11 +423,11 @@ void messaging_app_unsubscribe_endpoint(const char *app_name, const char *endpoi
 
 	AST_VECTOR_REMOVE_CMP_UNORDERED(&sub->applications, app_name, application_tuple_cmp, ao2_cleanup);
 	if (AST_VECTOR_SIZE(&sub->applications) == 0) {
-		if (!ast_strlen_zero(ast_endpoint_get_resource(endpoint))) {
+		if (endpoint && !ast_strlen_zero(ast_endpoint_get_resource(endpoint))) {
 			ao2_unlink(endpoint_subscriptions, sub);
 		} else {
 			ast_rwlock_wrlock(&tech_subscriptions_lock);
-			AST_VECTOR_REMOVE_CMP_UNORDERED(&tech_subscriptions, ast_endpoint_get_id(endpoint),
+			AST_VECTOR_REMOVE_CMP_UNORDERED(&tech_subscriptions, endpoint ? ast_endpoint_get_id(endpoint) : TECH_WILDCARD,
 				messaging_subscription_cmp, AST_VECTOR_ELEM_CLEANUP_NOOP);
 			ast_rwlock_unlock(&tech_subscriptions_lock);
 		}
@@ -429,9 +435,9 @@ void messaging_app_unsubscribe_endpoint(const char *app_name, const char *endpoi
 	ao2_unlock(sub);
 	ao2_ref(sub, -1);
 
-	ast_debug(3, "App '%s' unsubscribed to messages from endpoint '%s'\n", app_name, ast_endpoint_get_id(endpoint));
+	ast_debug(3, "App '%s' unsubscribed to messages from endpoint '%s'\n", app_name, endpoint ? ast_endpoint_get_id(endpoint) : "-- ALL --");
 	ast_test_suite_event_notify("StasisMessagingSubscription", "SubState: Unsubscribed\r\nAppName: %s\r\nToken: %s\r\n",
-		app_name, ast_endpoint_get_id(endpoint));
+		app_name, endpoint ? ast_endpoint_get_id(endpoint) : "ALL");
 }
 
 static struct message_subscription *get_or_create_subscription(struct ast_endpoint *endpoint)
@@ -442,12 +448,12 @@ static struct message_subscription *get_or_create_subscription(struct ast_endpoi
 		return sub;
 	}
 
-	sub = message_subscription_alloc(ast_endpoint_get_id(endpoint));
+	sub = message_subscription_alloc(endpoint ? ast_endpoint_get_id(endpoint) : TECH_WILDCARD);
 	if (!sub) {
 		return NULL;
 	}
 
-	if (!ast_strlen_zero(ast_endpoint_get_resource(endpoint))) {
+	if (endpoint && !ast_strlen_zero(ast_endpoint_get_resource(endpoint))) {
 		ao2_link(endpoint_subscriptions, sub);
 	} else {
 		ast_rwlock_wrlock(&tech_subscriptions_lock);
@@ -482,9 +488,9 @@ int messaging_app_subscribe_endpoint(const char *app_name, struct ast_endpoint *
 	AST_VECTOR_APPEND(&sub->applications, tuple);
 	ao2_unlock(sub);
 
-	ast_debug(3, "App '%s' subscribed to messages from endpoint '%s'\n", app_name, ast_endpoint_get_id(endpoint));
+	ast_debug(3, "App '%s' subscribed to messages from endpoint '%s'\n", app_name, endpoint ? ast_endpoint_get_id(endpoint) : "-- ALL --");
 	ast_test_suite_event_notify("StasisMessagingSubscription", "SubState: Subscribed\r\nAppName: %s\r\nToken: %s\r\n",
-		app_name, ast_endpoint_get_id(endpoint));
+		app_name, endpoint ? ast_endpoint_get_id(endpoint) : "ALL");
 
 	return 0;
 }
diff --git a/res/stasis/stasis_bridge.c b/res/stasis/stasis_bridge.c
index 9300bca..e410881 100644
--- a/res/stasis/stasis_bridge.c
+++ b/res/stasis/stasis_bridge.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 429062 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/bridge.h"
 #include "asterisk/bridge_after.h"
@@ -99,6 +99,56 @@ static void bridge_stasis_queue_join_action(struct ast_bridge *self,
 
 /*!
  * \internal
+ * \brief Peek at channel before it is pushed into bridge
+ * \since 13.2.0
+ *
+ * \param self Bridge to operate upon.
+ * \param bridge_channel Bridge channel to push.
+ * \param swap Bridge channel to swap places with if not NULL.
+ *
+ * \note On entry, self is already locked.
+ *
+ * \retval 0 on success.
+ * \retval -1 on failure.  The channel should not be pushed.
+ */
+static int bridge_stasis_push_peek(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
+{
+	struct stasis_app_control *swap_control;
+	struct ast_channel_snapshot *to_be_replaced;
+
+	if (!swap) {
+		goto done;
+	}
+
+	swap_control = stasis_app_control_find_by_channel(swap->chan);
+	if (!swap_control) {
+		ast_log(LOG_ERROR,"Failed to find stasis app control for swapped channel %s\n", ast_channel_name(swap->chan));
+		return -1;
+	}
+	to_be_replaced = ast_channel_snapshot_get_latest(ast_channel_uniqueid(swap->chan));
+
+	ast_debug(3, "Copying stasis app name %s from %s to %s\n", app_name(control_app(swap_control)),
+		ast_channel_name(swap->chan), ast_channel_name(bridge_channel->chan));
+
+	ast_channel_lock(bridge_channel->chan);
+
+	/* copy the app name from the swap channel */
+	app_set_replace_channel_app(bridge_channel->chan, app_name(control_app(swap_control)));
+
+	/* set the replace channel snapshot */
+	app_set_replace_channel_snapshot(bridge_channel->chan, to_be_replaced);
+
+	ast_channel_unlock(bridge_channel->chan);
+
+	ao2_ref(swap_control, -1);
+	ao2_cleanup(to_be_replaced);
+
+done:
+	return ast_bridge_base_v_table.push_peek(self, bridge_channel, swap);
+}
+
+/*!
+ * \internal
  * \brief Push this channel into the Stasis bridge.
  * \since 12.5.0
  *
@@ -125,8 +175,15 @@ static int bridge_stasis_push(struct ast_bridge *self, struct ast_bridge_channel
 		}
 
 		bridge_stasis_queue_join_action(self, bridge_channel);
+		if (swap) {
+			/* nudge the swap channel out of the bridge */
+			ast_bridge_channel_leave_bridge(swap, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, 0);
+		}
 
-		/* Return -1 so the push fails and the after-bridge callback gets called */
+		/* Return -1 so the push fails and the after-bridge callback gets called
+		 * This keeps the bridging framework from putting the channel into the bridge
+		 * until the Stasis thread gets started, and then the channel is put into the bridge.
+		 */
 		return -1;
 	}
 
@@ -228,4 +285,5 @@ void bridge_stasis_init(void)
 	bridge_stasis_v_table.name = "stasis";
 	bridge_stasis_v_table.push = bridge_stasis_push;
 	bridge_stasis_v_table.pull = bridge_stasis_pull;
+	bridge_stasis_v_table.push_peek = bridge_stasis_push_peek;
 }
diff --git a/res/stasis_recording/stored.c b/res/stasis_recording/stored.c
index 1b783e7..59c07f8 100644
--- a/res/stasis_recording/stored.c
+++ b/res/stasis_recording/stored.c
@@ -25,7 +25,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419022 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/astobj2.h"
 #include "asterisk/paths.h"
@@ -470,7 +470,7 @@ int stasis_app_stored_recording_copy(struct stasis_app_stored_recording *src_rec
 	/* Drop the extension if specified, core will do this for us */
 	format = strrchr(dst_file, '.');
 	if (format) {
-		format = '\0';
+		*format = '\0';
 	}
 
 	/* See if any intermediary directories need to be made */
diff --git a/rest-api-templates/api.wiki.mustache b/rest-api-templates/api.wiki.mustache
index de6de2d..0a54a64 100644
--- a/rest-api-templates/api.wiki.mustache
+++ b/rest-api-templates/api.wiki.mustache
@@ -11,21 +11,33 @@ h1. {{name_title}}
 {{#operations}}
 
 {anchor:{{nickname}}}
-h2. {{http_method}} {{wiki_path}}
+h2. {{nickname}}: {{http_method}} {{wiki_path}}
 
 {{{wiki_summary}}}{{#wiki_notes}} {{{wiki_notes}}}{{/wiki_notes}}
 {{#has_path_parameters}}
 
 h3. Path parameters
 {{#path_parameters}}
-* {{name}}: {{data_type}}{{#default_value}} = {{default_value}}{{/default_value}} - {{{wiki_description}}}
+* {{name}}: _{{data_type}}_ - {{{wiki_description}}}
+{{#default_value}}
+** Default: {{default_value}}
+{{/default_value}}
+{{#wiki_allowable_values}}
+** {{wiki_allowable_values}}
+{{/wiki_allowable_values}}
 {{/path_parameters}}
 {{/has_path_parameters}}
 {{#has_query_parameters}}
 
 h3. Query parameters
 {{#query_parameters}}
-* {{name}}: {{data_type}}{{#default_value}} = {{default_value}}{{/default_value}} -{{#required}} *(required)*{{/required}} {{{wiki_description}}}
+* {{name}}: _{{data_type}}_ -{{#required}} *(required)*{{/required}} {{{wiki_description}}}
+{{#default_value}}
+** Default: {{default_value}}
+{{/default_value}}
+{{#wiki_allowable_values}}
+** {{wiki_allowable_values}}
+{{/wiki_allowable_values}}
 {{#allow_multiple}}
 ** Allows comma separated values.
 {{/allow_multiple}}
@@ -55,7 +67,7 @@ h3. Header parameters
 
 h3. Error Responses
 {{#error_responses}}
-* {{code}} - {{{reason}}}
+* {{code}} - {{{wiki_reason}}}
 {{/error_responses}}
 {{/has_error_responses}}
 {{/operations}}
diff --git a/rest-api-templates/ari.make.mustache b/rest-api-templates/ari.make.mustache
index 50293cf..0e1b7a0 100644
--- a/rest-api-templates/ari.make.mustache
+++ b/rest-api-templates/ari.make.mustache
@@ -20,6 +20,7 @@
 
 {{#apis}}
 res_ari_{{c_name}}.so: ari/resource_{{c_name}}.o
+.res_ari_{{c_name}}.moduleinfo: ari/resource_{{c_name}}.c
 
 ari/resource_{{c_name}}.o: _ASTCFLAGS+=$(call MOD_ASTCFLAGS,res_ari_{{c_name}})
 
diff --git a/rest-api-templates/ari_model_validators.c.mustache b/rest-api-templates/ari_model_validators.c.mustache
index 9276478..0fabbcf 100644
--- a/rest-api-templates/ari_model_validators.c.mustache
+++ b/rest-api-templates/ari_model_validators.c.mustache
@@ -22,12 +22,12 @@
  /*
 {{> do-not-edit}}
  * This file is generated by a mustache template. Please see the original
- * template in rest-api-templates/ari_model_validators.h.mustache
+ * template in rest-api-templates/ari_model_validators.c.mustache
  */
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 401834 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/logger.h"
 #include "asterisk/module.h"
@@ -50,7 +50,7 @@ int ast_ari_validate_{{c_id}}(struct ast_json *json)
 
 	discriminator = ast_json_string_get(ast_json_object_get(json, "{{discriminator.name}}"));
 	if (!discriminator) {
-		ast_log(LOG_ERROR, "ARI {{id}} missing required field {{discriminator.name}}");
+		ast_log(LOG_ERROR, "ARI {{id}} missing required field {{discriminator.name}}\n");
 		return 0;
 	}
 
diff --git a/rest-api-templates/ari_resource.c.mustache b/rest-api-templates/ari_resource.c.mustache
index 85cf603..e2b2342 100644
--- a/rest-api-templates/ari_resource.c.mustache
+++ b/rest-api-templates/ari_resource.c.mustache
@@ -26,7 +26,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 402994 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "resource_{{c_name}}.h"
 
diff --git a/rest-api-templates/ari_resource.h.mustache b/rest-api-templates/ari_resource.h.mustache
index 3a20776..d3f40b6 100644
--- a/rest-api-templates/ari_resource.h.mustache
+++ b/rest-api-templates/ari_resource.h.mustache
@@ -89,6 +89,23 @@ int ast_ari_{{c_name}}_{{c_nickname}}_parse_body(
 void ast_ari_{{c_name}}_{{c_nickname}}(struct ast_variable *headers, struct ast_ari_{{c_name}}_{{c_nickname}}_args *args, struct ast_ari_response *response);
 {{/is_req}}
 {{#is_websocket}}
+
+/*!
+ * \brief {{summary}}
+{{#notes}}
+ *
+ * {{{notes}}}
+{{/notes}}
+ *
+ * \param ser HTTP TCP/TLS Server Session
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ *
+ * \retval 0 success
+ * \retval non-zero error
+ */
+int ast_ari_websocket_{{c_name}}_{{c_nickname}}_attempted(struct ast_tcptls_session_instance *ser, struct ast_variable *headers, struct ast_ari_{{c_name}}_{{c_nickname}}_args *args);
+
 /*!
  * \brief {{summary}}
 {{#notes}}
@@ -100,7 +117,7 @@ void ast_ari_{{c_name}}_{{c_nickname}}(struct ast_variable *headers, struct ast_
  * \param headers HTTP headers.
  * \param args Swagger parameters.
  */
-void ast_ari_websocket_{{c_name}}_{{c_nickname}}(struct ast_ari_websocket_session *session, struct ast_variable *headers, struct ast_ari_{{c_name}}_{{c_nickname}}_args *args);
+void ast_ari_websocket_{{c_name}}_{{c_nickname}}_established(struct ast_ari_websocket_session *session, struct ast_variable *headers, struct ast_ari_{{c_name}}_{{c_nickname}}_args *args);
 {{/is_websocket}}
 {{/operations}}
 {{/apis}}
diff --git a/rest-api-templates/asterisk_processor.py b/rest-api-templates/asterisk_processor.py
index ef0f167..68a6799 100644
--- a/rest-api-templates/asterisk_processor.py
+++ b/rest-api-templates/asterisk_processor.py
@@ -199,6 +199,8 @@ class AsteriskProcessor(SwaggerPostProcessor):
             raise SwaggerError("Summary should end with .", context)
         operation.wiki_summary = wikify(operation.summary or "")
         operation.wiki_notes = wikify(operation.notes or "")
+        for error_response in operation.error_responses:
+            error_response.wiki_reason = wikify(error_response.reason or "")
         operation.parse_body = (operation.body_parameter or operation.has_query_parameters) and True
 
     def process_parameter(self, parameter, context):
@@ -223,6 +225,10 @@ class AsteriskProcessor(SwaggerPostProcessor):
         else:
             parameter.c_space = ' '
         parameter.wiki_description = wikify(parameter.description)
+        if parameter.allowable_values:
+            parameter.wiki_allowable_values = parameter.allowable_values.to_wiki()
+        else:
+            parameter.wiki_allowable_values = None
 
     def process_model(self, model, context):
         model.description_dox = model.description.replace('\n', '\n * ')
diff --git a/rest-api-templates/param_parsing.mustache b/rest-api-templates/param_parsing.mustache
index 2dde4b3..247c121 100644
--- a/rest-api-templates/param_parsing.mustache
+++ b/rest-api-templates/param_parsing.mustache
@@ -101,7 +101,7 @@
 		}
 	}
 {{#body_parameter}}
-	args.{{c_name}} = ast_json_ref(body);
+	args.{{c_name}} = body;
 {{/body_parameter}}
 {{^body_parameter}}
 	if (ast_ari_{{c_name}}_{{c_nickname}}_parse_body(body, &args)) {
diff --git a/rest-api-templates/res_ari_resource.c.mustache b/rest-api-templates/res_ari_resource.c.mustache
index abc1a59..08f6204 100644
--- a/rest-api-templates/res_ari_resource.c.mustache
+++ b/rest-api-templates/res_ari_resource.c.mustache
@@ -38,13 +38,14 @@
 
 /*** MODULEINFO
 	<depend type="module">res_ari</depend>
+	<depend type="module">res_ari_model</depend>
 	<depend type="module">res_stasis</depend>
 	<support_level>core</support_level>
  ***/
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420536 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/app.h"
 #include "asterisk/module.h"
@@ -136,7 +137,52 @@ fin: __attribute__((unused))
 }
 {{/is_req}}
 {{#is_websocket}}
-static void ast_ari_{{c_name}}_{{c_nickname}}_ws_cb(struct ast_websocket *ws_session,
+static int ast_ari_{{c_name}}_{{c_nickname}}_ws_attempted_cb(struct ast_tcptls_session_instance *ser, struct ast_variable *get_params, struct ast_variable *headers)
+{
+	struct ast_ari_{{c_name}}_{{c_nickname}}_args args = {};
+{{#has_parameters}}
+	int res = 0;
+	RAII_VAR(struct ast_ari_response *, response, NULL, ast_free);
+	struct ast_variable *i;
+{{/has_parameters}}
+
+{{#has_parameters}}
+	response = ast_calloc(1, sizeof(*response));
+	if (!response) {
+		ast_log(LOG_ERROR, "Failed to create response.\n");
+		goto fin;
+	}
+{{/has_parameters}}
+
+{{> param_parsing}}
+
+	res = ast_ari_websocket_{{c_name}}_{{c_nickname}}_attempted(ser, headers, &args);
+
+fin: __attribute__((unused))
+	if (!response) {
+		ast_http_error(ser, 500, "Server Error", "Memory allocation error");
+		res = -1;
+	} else if (response->response_code != 0) {
+		/* Param parsing failure */
+		RAII_VAR(char *, msg, NULL, ast_json_free);
+		if (response->message) {
+			msg = ast_json_dump_string(response->message);
+		} else {
+			ast_log(LOG_ERROR, "Missing response message\n");
+		}
+
+		if (msg) {
+			ast_http_error(ser, response->response_code, response->response_text, msg);
+		}
+		res = -1;
+	}
+{{> param_cleanup}}
+{{#has_parameters}}
+	return res;
+{{/has_parameters}}
+}
+
+static void ast_ari_{{c_name}}_{{c_nickname}}_ws_established_cb(struct ast_websocket *ws_session,
 	struct ast_variable *get_params, struct ast_variable *headers)
 {
 	struct ast_ari_{{c_name}}_{{c_nickname}}_args args = {};
@@ -174,16 +220,11 @@ static void ast_ari_{{c_name}}_{{c_nickname}}_ws_cb(struct ast_websocket *ws_ses
 
 {{> param_parsing}}
 
-	ast_ari_websocket_{{c_name}}_{{c_nickname}}(session, headers, &args);
+	ast_ari_websocket_{{c_name}}_{{c_nickname}}_established(session, headers, &args);
 
 fin: __attribute__((unused))
 	if (response && response->response_code != 0) {
 		/* Param parsing failure */
-		/* TODO - ideally, this would return the error code to the
-		 * HTTP client; but we've already done the WebSocket
-		 * negotiation. Param parsing should happen earlier, but we
-		 * need a way to pass it through the WebSocket code to the
-		 * callback */
 		RAII_VAR(char *, msg, NULL, ast_json_free);
 		if (response->message) {
 			msg = ast_json_dump_string(response->message);
@@ -210,16 +251,26 @@ static int load_module(void)
 {
 	int res = 0;
 {{#apis}}
+{{#operations}}
 {{#has_websocket}}
+	struct ast_websocket_protocol *protocol;
+
 	{{full_name}}.ws_server = ast_websocket_server_create();
 	if (!{{full_name}}.ws_server) {
 		return AST_MODULE_LOAD_FAILURE;
 	}
+
+	protocol = ast_websocket_sub_protocol_alloc("{{websocket_protocol}}");
+	if (!protocol) {
+		ao2_ref({{full_name}}.ws_server, -1);
+		{{full_name}}.ws_server = NULL;
+		return AST_MODULE_LOAD_FAILURE;
+	}
+	protocol->session_attempted = ast_ari_{{c_name}}_{{c_nickname}}_ws_attempted_cb;
+	protocol->session_established = ast_ari_{{c_name}}_{{c_nickname}}_ws_established_cb;
 {{/has_websocket}}
-{{#operations}}
 {{#is_websocket}}
-	res |= ast_websocket_server_add_protocol({{full_name}}.ws_server,
-		"{{websocket_protocol}}", ast_ari_{{c_name}}_{{c_nickname}}_ws_cb);
+	res |= ast_websocket_server_add_protocol2({{full_name}}.ws_server, protocol);
 {{/is_websocket}}
 {{/operations}}
 {{/apis}}
diff --git a/rest-api-templates/swagger_model.py b/rest-api-templates/swagger_model.py
index 9c65219..f3b49e1 100644
--- a/rest-api-templates/swagger_model.py
+++ b/rest-api-templates/swagger_model.py
@@ -220,6 +220,9 @@ class AllowableRange(Stringify):
         self.min_value = min_value
         self.max_value = max_value
 
+    def to_wiki(self):
+        return "Allowed range: Min: {0}; Max: {1}".format(self.min_value, self.max_value)
+
 
 class AllowableList(Stringify):
     """Model of a allowableValues of type LIST
@@ -229,6 +232,9 @@ class AllowableList(Stringify):
     def __init__(self, values):
         self.values = values
 
+    def to_wiki(self):
+        return "Allowed values: {0}".format(", ".join(self.values))
+
 
 def load_allowable_values(json, context):
     """Parse a JSON allowableValues object.
diff --git a/rest-api/api-docs/applications.json b/rest-api/api-docs/applications.json
index db5a9ee..c8660cf 100644
--- a/rest-api/api-docs/applications.json
+++ b/rest-api/api-docs/applications.json
@@ -1,8 +1,8 @@
 {
 	"_copyright": "Copyright (C) 2013, Digium, Inc.",
 	"_author": "David M. Lee, II <dlee at digium.com>",
-	"_svn_revision": "$Revision: 429091 $",
-	"apiVersion": "1.6.0",
+	"_svn_revision": "$Revision$",
+	"apiVersion": "1.9.0",
 	"swaggerVersion": "1.1",
 	"basePath": "http://localhost:8088/ari",
 	"resourcePath": "/api-docs/applications.{format}",
diff --git a/rest-api/api-docs/asterisk.json b/rest-api/api-docs/asterisk.json
index 69cef05..9dbf382 100644
--- a/rest-api/api-docs/asterisk.json
+++ b/rest-api/api-docs/asterisk.json
@@ -1,13 +1,153 @@
 {
 	"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
 	"_author": "David M. Lee, II <dlee at digium.com>",
-	"_svn_revision": "$Revision: 429091 $",
-	"apiVersion": "1.6.0",
+	"_svn_revision": "$Revision$",
+	"apiVersion": "1.9.0",
 	"swaggerVersion": "1.1",
 	"basePath": "http://localhost:8088/ari",
 	"resourcePath": "/api-docs/asterisk.{format}",
 	"apis": [
 		{
+			"path": "/asterisk/config/dynamic/{configClass}/{objectType}/{id}",
+			"description": "Asterisk dynamic configuration",
+			"operations": [
+				{
+					"httpMethod": "GET",
+					"summary": "Retrieve a dynamic configuration object.",
+					"nickname": "getObject",
+					"responseClass": "List[ConfigTuple]",
+					"parameters": [
+						{
+							"name": "configClass",
+							"description": "The configuration class containing dynamic configuration objects.",
+							"paramType": "path",
+							"required": true,
+							"allowMultiple": false,
+							"dataType": "string"
+						},
+						{
+							"name": "objectType",
+							"description": "The type of configuration object to retrieve.",
+							"paramType": "path",
+							"required": true,
+							"allowMultiple": false,
+							"dataType": "string"
+						},
+						{
+							"name": "id",
+							"description": "The unique identifier of the object to retrieve.",
+							"paramType": "path",
+							"required": true,
+							"allowMultiple": false,
+							"dataType": "string"
+						}
+					],
+					"errorResponses": [
+						{
+							"code": 404,
+							"reason": "{configClass|objectType|id} not found"
+						}
+					]
+				},
+				{
+					"httpMethod": "PUT",
+					"summary": "Create or update a dynamic configuration object.",
+					"nickname": "updateObject",
+					"responseClass": "List[ConfigTuple]",
+					"parameters": [
+						{
+							"name": "configClass",
+							"description": "The configuration class containing dynamic configuration objects.",
+							"paramType": "path",
+							"required": true,
+							"allowMultiple": false,
+							"dataType": "string"
+						},
+						{
+							"name": "objectType",
+							"description": "The type of configuration object to create or update.",
+							"paramType": "path",
+							"required": true,
+							"allowMultiple": false,
+							"dataType": "string"
+						},
+						{
+							"name": "id",
+							"description": "The unique identifier of the object to create or update.",
+							"paramType": "path",
+							"required": true,
+							"allowMultiple": false,
+							"dataType": "string"
+						},
+						{
+							"name": "fields",
+							"description": "The body object should have a value that is a list of ConfigTuples, which provide the fields to update. Ex. [ { \"attribute\": \"directmedia\", \"value\": \"false\" } ]",
+							"paramType": "body",
+							"required": false,
+							"dataType": "containers",
+							"allowMultiple": false
+						}
+					],
+					"errorResponses": [
+						{
+							"code": 400,
+							"reason": "Bad request body"
+						},
+						{
+							"code": 403,
+							"reason": "Could not create or update object"
+						},
+						{
+							"code": 404,
+							"reason": "{configClass|objectType} not found"
+						}
+					]
+				},
+				{
+					"httpMethod": "DELETE",
+					"summary": "Delete a dynamic configuration object.",
+					"nickname": "deleteObject",
+					"responseClass": "void",
+					"parameters": [
+						{
+							"name": "configClass",
+							"description": "The configuration class containing dynamic configuration objects.",
+							"paramType": "path",
+							"required": true,
+							"allowMultiple": false,
+							"dataType": "string"
+						},
+						{
+							"name": "objectType",
+							"description": "The type of configuration object to delete.",
+							"paramType": "path",
+							"required": true,
+							"allowMultiple": false,
+							"dataType": "string"
+						},
+						{
+							"name": "id",
+							"description": "The unique identifier of the object to delete.",
+							"paramType": "path",
+							"required": true,
+							"allowMultiple": false,
+							"dataType": "string"
+						}
+					],
+					"errorResponses": [
+						{
+							"code": 403,
+							"reason": "Could not delete object"
+						},
+						{
+							"code": 404,
+							"reason": "{configClass|objectType|id} not found"
+						}
+					]
+				}
+			]
+		},
+		{
 			"path": "/asterisk/info",
 			"description": "Asterisk system information (similar to core show settings)",
 			"operations": [
@@ -39,6 +179,226 @@
 			]
 		},
 		{
+			"path": "/asterisk/modules",
+			"description": "Asterisk modules",
+			"operations": [
+				{
+					"httpMethod": "GET",
+					"summary": "List Asterisk modules.",
+					"nickname": "listModules",
+					"responseClass": "List[Module]"
+				}
+			]
+		},
+		{
+			"path": "/asterisk/modules/{moduleName}",
+			"description": "Asterisk module",
+			"operations": [
+				{
+					"httpMethod": "GET",
+					"summary": "Get Asterisk module information.",
+					"nickname": "getModule",
+					"responseClass": "Module",
+					"parameters": [
+						{
+							"name": "moduleName",
+							"description": "Module's name",
+							"paramType": "path",
+							"required": true,
+							"allowMultiple": false,
+							"dataType": "string"
+						}
+					],
+					"errorResponses": [
+						{
+							"code": 404,
+							"reason": "Module could not be found in running modules."
+						},
+						{
+							"code": 409,
+							"reason": "Module information could not be retrieved."
+						}
+					]
+				},
+				{
+					"httpMethod": "POST",
+					"summary": "Load an Asterisk module.",
+					"nickname": "loadModule",
+					"responseClass": "void",
+					"parameters": [
+						{
+							"name": "moduleName",
+							"description": "Module's name",
+							"paramType": "path",
+							"required": true,
+							"allowMultiple": false,
+							"dataType": "string"
+						}
+					],
+					"errorResponses": [
+						{
+							"code": 409,
+							"reason": "Module could not be loaded."
+						}
+					]
+				},
+				{
+					"httpMethod": "DELETE",
+					"summary": "Unload an Asterisk module.",
+					"nickname": "unloadModule",
+					"responseClass": "void",
+					"parameters": [
+						{
+							"name": "moduleName",
+							"description": "Module's name",
+							"paramType": "path",
+							"required": true,
+							"allowMultiple": false,
+							"dataType": "string"
+						}
+					],
+					"errorResponses": [
+						{
+							"code": 404,
+							"reason": "Module not found in running modules."
+						},
+						{
+							"code": 409,
+							"reason": "Module could not be unloaded."
+						}
+					]
+				},
+				{
+					"httpMethod": "PUT",
+					"summary": "Reload an Asterisk module.",
+					"nickname": "reloadModule",
+					"responseClass": "void",
+					"parameters": [
+						{
+							"name": "moduleName",
+							"description": "Module's name",
+							"paramType": "path",
+							"required": true,
+							"allowMultiple": false,
+							"dataType": "string"
+						}
+					],
+					"errorResponses": [
+						{
+							"code": 404,
+							"reason": "Module not found in running modules."
+						},
+						{
+							"code": 409,
+							"reason": "Module could not be reloaded."
+						}
+					]
+				}
+			]
+		},
+		{
+			"path": "/asterisk/logging",
+			"description": "Asterisk log channels",
+			"operations": [
+				{
+					"httpMethod": "GET",
+					"summary": "Gets Asterisk log channel information.",
+					"nickname": "listLogChannels",
+					"responseClass": "List[LogChannel]"
+				}
+			]
+		},
+		{
+			"path": "/asterisk/logging/{logChannelName}",
+			"description": "Asterisk log channel",
+			"operations": [
+				{
+					"httpMethod": "POST",
+					"summary": "Adds a log channel.",
+					"nickname": "addLog",
+					"responseClass": "void",
+					"parameters": [
+						{
+							"name": "logChannelName",
+							"description": "The log channel to add",
+							"paramType": "path",
+							"required": true,
+							"allowMultiple": false,
+							"dataType": "string"
+						},
+						{
+							"name": "configuration",
+							"description": "levels of the log channel",
+							"paramType": "query",
+							"required": true,
+							"allowMultiple": false,
+							"dataType": "string"
+						}
+					],
+					"errorResponses": [
+						{
+							"code": 400,
+							"reason": "Bad request body"
+						},
+						{
+							"code": 409,
+							"reason": "Log channel could not be created."
+						}
+					]
+				},
+				{
+					"httpMethod": "DELETE",
+					"summary": "Deletes a log channel.",
+					"nickname": "deleteLog",
+					"responseClass": "void",
+					"parameters": [
+						{
+							"name": "logChannelName",
+							"description": "Log channels name",
+							"paramType": "path",
+							"required": true,
+							"allowMultiple": false,
+							"dataType": "string"
+						}
+					],
+					"errorResponses": [
+						{
+							"code": 404,
+							"reason": "Log channel does not exist."
+						}
+					]
+				}
+			]
+		},
+		{
+			"path": "/asterisk/logging/{logChannelName}/rotate",
+			"description": "Asterisk log channel",
+			"operations": [
+				{
+					"httpMethod": "PUT",
+					"summary": "Rotates a log channel.",
+					"nickname": "rotateLog",
+					"responseClass": "void",
+					"parameters": [
+						{
+							"name": "logChannelName",
+							"description": "Log channel's name",
+							"paramType": "path",
+							"required": true,
+							"allowMultiple": false,
+							"dataType": "string"
+						}
+					],
+					"errorResponses": [
+						{
+							"code": 404,
+							"reason": "Log channel does not exist."
+						}
+					]
+				}
+			]
+		},
+		{
 			"path": "/asterisk/variable",
 			"description": "Global variables",
 			"operations": [
@@ -244,6 +604,63 @@
 				}
 			}
 		},
+		"Module": {
+			"id": "Module",
+			"description": "Details of an Asterisk module",
+			"properties": {
+				"name": {
+					"type": "string",
+					"description": "The name of this module",
+					"required": true
+				},
+				"description": {
+					"type": "string",
+					"description": "The description of this module",
+					"required": true
+				},
+				"use_count": {
+					"type": "int",
+					"description": "The number of times this module is being used",
+					"required": true
+				},
+				"status": {
+					"type": "string",
+					"description": "The running status of this module",
+					"required": true
+				},
+				"support_level": {
+					"type": "string",
+					"description": "The support state of this module",
+					"required": true
+				}
+			}
+		},
+		"LogChannel": {
+			"id": "LogChannel",
+			"description": "Details of an Asterisk log channel",
+			"properties": {
+				"channel": {
+					"type": "string",
+					"description": "The log channel path",
+					"required": true
+				},
+				"type": {
+					"type": "string",
+					"description": "Types of logs for the log channel",
+					"required": true
+				},
+				"status": {
+					"type": "string",
+					"description": "Whether or not a log type is enabled",
+					"required": true
+				},
+				"configuration": {
+					"type": "string",
+					"description": "The various log levels",
+					"required": true
+				}
+			}
+		},
 		"Variable": {
 			"id": "Variable",
 			"description": "The value of a channel variable",
@@ -254,6 +671,22 @@
 					"description": "The value of the variable requested"
 				}
 			}
+		},
+		"ConfigTuple": {
+			"id": "ConfigTuple",
+			"description": "A key/value pair that makes up part of a configuration object.",
+			"properties": {
+				"attribute": {
+					"required": true,
+					"type": "string",
+					"description": "A configuration object attribute."
+				},
+				"value": {
+					"required": true,
+					"type": "string",
+					"description": "The value for the attribute."
+				}
+			}
 		}
 	}
 }
diff --git a/rest-api/api-docs/bridges.json b/rest-api/api-docs/bridges.json
index 9545416..0e726a5 100644
--- a/rest-api/api-docs/bridges.json
+++ b/rest-api/api-docs/bridges.json
@@ -1,8 +1,8 @@
 {
 	"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
 	"_author": "David M. Lee, II <dlee at digium.com>",
-	"_svn_revision": "$Revision: 429091 $",
-	"apiVersion": "1.6.0",
+	"_svn_revision": "$Revision$",
+	"apiVersion": "1.9.0",
 	"swaggerVersion": "1.1",
 	"basePath": "http://localhost:8088/ari",
 	"resourcePath": "/api-docs/bridges.{format}",
@@ -60,7 +60,7 @@
 					"httpMethod": "POST",
 					"summary": "Create a new bridge or updates an existing one.",
 					"notes": "This bridge persists until it has been shut down, or Asterisk has been shut down.",
-					"nickname": "create_or_update_with_id",
+					"nickname": "createWithId",
 					"responseClass": "Bridge",
 					"parameters": [
 						{
@@ -398,7 +398,7 @@
 				{
 					"httpMethod": "POST",
 					"summary": "Start playback of media on a bridge.",
-					"notes": "The media URI may be any of a number of URI's. Currently sound: and recording: URI's are supported. This operation creates a playback resource that can be used to control the playback of media (pause, rewind, fast forward, etc.)",
+					"notes": "The media URI may be any of a number of URI's. Currently sound:, recording:, number:, digits:, characters:, and tone: URI's are supported. This operation creates a playback resource that can be used to control the playback of media (pause, rewind, fast forward, etc.)",
 					"nickname": "playWithId",
 					"responseClass": "Playback",
 					"parameters": [
diff --git a/rest-api/api-docs/channels.json b/rest-api/api-docs/channels.json
index ac497e9..cb41fb6 100644
--- a/rest-api/api-docs/channels.json
+++ b/rest-api/api-docs/channels.json
@@ -1,8 +1,8 @@
 {
 	"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
 	"_author": "David M. Lee, II <dlee at digium.com>",
-	"_svn_revision": "$Revision: 429091 $",
-	"apiVersion": "1.6.0",
+	"_svn_revision": "$Revision$",
+	"apiVersion": "1.9.0",
 	"swaggerVersion": "1.1",
 	"basePath": "http://localhost:8088/ari",
 	"resourcePath": "/api-docs/channels.{format}",
@@ -34,7 +34,7 @@
 						},
 						{
 							"name": "extension",
-							"description": "The extension to dial after the endpoint answers",
+							"description": "The extension to dial after the endpoint answers. Mutually exclusive with 'app'.",
 							"paramType": "query",
 							"required": false,
 							"allowMultiple": false,
@@ -42,7 +42,7 @@
 						},
 						{
 							"name": "context",
-							"description": "The context to dial after the endpoint answers. If omitted, uses 'default'",
+							"description": "The context to dial after the endpoint answers. If omitted, uses 'default'. Mutually exclusive with 'app'.",
 							"paramType": "query",
 							"required": false,
 							"allowMultiple": false,
@@ -50,15 +50,23 @@
 						},
 						{
 							"name": "priority",
-							"description": "The priority to dial after the endpoint answers. If omitted, uses 1",
+							"description": "The priority to dial after the endpoint answers. If omitted, uses 1. Mutually exclusive with 'app'.",
 							"paramType": "query",
 							"required": false,
 							"allowMultiple": false,
 							"dataType": "long"
 						},
 						{
+							"name": "label",
+							"description": "The label to dial after the endpoint answers. Will supersede 'priority' if provided. Mutually exclusive with 'app'.",
+							"paramType": "query",
+							"required": false,
+							"allowMultiple": false,
+							"dataType": "string"
+						},
+						{
 							"name": "app",
-							"description": "The application that is subscribed to the originated channel, and passed to the Stasis application.",
+							"description": "The application that is subscribed to the originated channel. When the channel is answered, it will be passed to this Stasis application. Mutually exclusive with 'context', 'extension', 'priority', and 'label'.",
 							"paramType": "query",
 							"required": false,
 							"allowMultiple": false,
@@ -66,7 +74,7 @@
 						},
 						{
 							"name": "appArgs",
-							"description": "The application arguments to pass to the Stasis application.",
+							"description": "The application arguments to pass to the Stasis application provided by 'app'. Mutually exclusive with 'context', 'extension', 'priority', and 'label'.",
 							"paramType": "query",
 							"required": false,
 							"allowMultiple": false,
@@ -112,6 +120,14 @@
 							"required": false,
 							"allowMultiple": false,
 							"dataType": "string"
+						},
+						{
+							"name": "originator",
+							"description": "The unique id of the channel which is originating this one.",
+							"paramType": "query",
+							"required": false,
+							"allowMultiple": false,
+							"dataType": "string"
 						}
 					],
 					"errorResponses": [
@@ -174,7 +190,7 @@
 						},
 						{
 							"name": "extension",
-							"description": "The extension to dial after the endpoint answers",
+							"description": "The extension to dial after the endpoint answers. Mutually exclusive with 'app'.",
 							"paramType": "query",
 							"required": false,
 							"allowMultiple": false,
@@ -182,7 +198,7 @@
 						},
 						{
 							"name": "context",
-							"description": "The context to dial after the endpoint answers. If omitted, uses 'default'",
+							"description": "The context to dial after the endpoint answers. If omitted, uses 'default'. Mutually exclusive with 'app'.",
 							"paramType": "query",
 							"required": false,
 							"allowMultiple": false,
@@ -190,15 +206,23 @@
 						},
 						{
 							"name": "priority",
-							"description": "The priority to dial after the endpoint answers. If omitted, uses 1",
+							"description": "The priority to dial after the endpoint answers. If omitted, uses 1. Mutually exclusive with 'app'.",
 							"paramType": "query",
 							"required": false,
 							"allowMultiple": false,
 							"dataType": "long"
 						},
 						{
+							"name": "label",
+							"description": "The label to dial after the endpoint answers. Will supersede 'priority' if provided. Mutually exclusive with 'app'.",
+							"paramType": "query",
+							"required": false,
+							"allowMultiple": false,
+							"dataType": "string"
+						},
+						{
 							"name": "app",
-							"description": "The application that is subscribed to the originated channel, and passed to the Stasis application.",
+							"description": "The application that is subscribed to the originated channel. When the channel is answered, it will be passed to this Stasis application. Mutually exclusive with 'context', 'extension', 'priority', and 'label'.",
 							"paramType": "query",
 							"required": false,
 							"allowMultiple": false,
@@ -206,7 +230,7 @@
 						},
 						{
 							"name": "appArgs",
-							"description": "The application arguments to pass to the Stasis application.",
+							"description": "The application arguments to pass to the Stasis application provided by 'app'. Mutually exclusive with 'context', 'extension', 'priority', and 'label'.",
 							"paramType": "query",
 							"required": false,
 							"allowMultiple": false,
@@ -244,6 +268,14 @@
 							"required": false,
 							"allowMultiple": false,
 							"dataType": "string"
+						},
+						{
+							"name": "originator",
+							"description": "The unique id of the channel which is originating this one.",
+							"paramType": "query",
+							"required": false,
+							"allowMultiple": false,
+							"dataType": "string"
 						}
 					],
 					"errorResponses": [
@@ -281,7 +313,8 @@
 								"values": [
 									"normal",
 									"busy",
-									"congestion"
+									"congestion",
+									"no_answer"
 								]
 							}
 						}
@@ -340,6 +373,14 @@
 							"required": false,
 							"allowMultiple": false,
 							"dataType": "int"
+						},
+						{
+							"name": "label",
+							"description": "The label to continue to - will supersede 'priority' if both are provided.",
+							"paramType": "query",
+							"required": false,
+							"allowMultiple": false,
+							"dataType": "string"
 						}
 					],
 					"errorResponses": [
@@ -356,6 +397,54 @@
 			]
 		},
 		{
+			"path": "/channels/{channelId}/redirect",
+			"description": "Inform the channel that it should redirect itself to a different location. Note that this will almost certainly cause the channel to exit the application.",
+			"operations": [
+				{
+					"httpMethod": "POST",
+					"summary": "Redirect the channel to a different location.",
+					"nickname": "redirect",
+					"responseClass": "void",
+					"parameters": [
+						{
+							"name": "channelId",
+							"description": "Channel's id",
+							"paramType": "path",
+							"required": true,
+							"allowMultiple": false,
+							"dataType": "string"
+						},
+						{
+							"name": "endpoint",
+							"description": "The endpoint to redirect the channel to",
+							"paramType": "query",
+							"required": true,
+							"allowMultiple": false,
+							"dataType": "string"
+						}
+					],
+					"errorResponses": [
+						{
+							"code": 400,
+							"reason": "Endpoint parameter not provided"
+						},
+						{
+							"code": 404,
+							"reason": "Channel or endpoint not found"
+						},
+						{
+							"code": 409,
+							"reason": "Channel not in a Stasis application"
+						},
+						{
+							"code": 422,
+							"reason": "Endpoint is not the same type as the channel"
+						}
+					]
+				}
+			]
+		},
+		{
 			"path": "/channels/{channelId}/answer",
 			"description": "Answer a channel",
 			"operations": [
@@ -882,7 +971,7 @@
 				{
 					"httpMethod": "POST",
 					"summary": "Start playback of media and specify the playbackId.",
-					"notes": "The media URI may be any of a number of URI's. Currently sound: and recording: URI's are supported. This operation creates a playback resource that can be used to control the playback of media (pause, rewind, fast forward, etc.)",
+					"notes": "The media URI may be any of a number of URI's. Currently sound:, recording:, number:, digits:, characters:, and tone: URI's are supported. This operation creates a playback resource that can be used to control the playback of media (pause, rewind, fast forward, etc.)",
 					"nickname": "playWithId",
 					"responseClass": "Playback",
 					"parameters": [
@@ -1110,7 +1199,7 @@
 						},
 						{
 							"code": 404,
-							"reason": "Channel not found"
+							"reason": "Channel or variable not found"
 						},
 						{
 							"code": 409,
@@ -1449,6 +1538,11 @@
 					"required": true,
 					"type": "Date",
 					"description": "Timestamp when channel was created"
+				},
+				"language": {
+					"required": true,
+					"type": "string",
+					"description": "The default spoken language"
 				}
 			}
 		}
diff --git a/rest-api/api-docs/deviceStates.json b/rest-api/api-docs/deviceStates.json
index 62ecb13..a268a18 100644
--- a/rest-api/api-docs/deviceStates.json
+++ b/rest-api/api-docs/deviceStates.json
@@ -1,8 +1,8 @@
 {
 	"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
 	"_author": "Kevin Harwell <kharwell at digium.com>",
-	"_svn_revision": "$Revision: 429091 $",
-	"apiVersion": "1.6.0",
+	"_svn_revision": "$Revision$",
+	"apiVersion": "1.9.0",
 	"swaggerVersion": "1.1",
 	"basePath": "http://localhost:8088/ari",
 	"resourcePath": "/api-docs/deviceStates.{format}",
diff --git a/rest-api/api-docs/endpoints.json b/rest-api/api-docs/endpoints.json
index 8440d8b..c6a3020 100644
--- a/rest-api/api-docs/endpoints.json
+++ b/rest-api/api-docs/endpoints.json
@@ -1,8 +1,8 @@
 {
 	"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
 	"_author": "David M. Lee, II <dlee at digium.com>",
-	"_svn_revision": "$Revision: 429091 $",
-	"apiVersion": "1.6.0",
+	"_svn_revision": "$Revision$",
+	"apiVersion": "1.9.0",
 	"swaggerVersion": "1.1",
 	"basePath": "http://localhost:8088/ari",
 	"resourcePath": "/api-docs/endpoints.{format}",
@@ -64,6 +64,10 @@
 					],
 					"errorResponses": [
 						{
+							"code": 400,
+							"reason": "Invalid parameters for sending a message."
+						},
+						{
 							"code": 404,
 							"reason": "Endpoint not found"
 						}
diff --git a/rest-api/api-docs/events.json b/rest-api/api-docs/events.json
index 9c24dbd..464e06f 100644
--- a/rest-api/api-docs/events.json
+++ b/rest-api/api-docs/events.json
@@ -1,8 +1,8 @@
 {
 	"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
 	"_author": "David M. Lee, II <dlee at digium.com>",
-	"_svn_revision": "$Revision: 429091 $",
-	"apiVersion": "1.6.0",
+	"_svn_revision": "$Revision$",
+	"apiVersion": "1.9.0",
 	"swaggerVersion": "1.2",
 	"basePath": "http://localhost:8088/ari",
 	"resourcePath": "/api-docs/events.{format}",
@@ -26,6 +26,14 @@
 							"required": true,
 							"allowMultiple": true,
 							"dataType": "string"
+						},
+						{
+							"name": "subscribeAll",
+							"description": "Subscribe to all Asterisk events. If provided, the applications listed will be subscribed to all events, effectively disabling the application specific subscriptions. Default is 'false'.",
+							"paramType": "query",
+							"required": false,
+							"allowMultiple": false,
+							"dataType": "boolean"
 						}
 					]
 				}
@@ -161,14 +169,85 @@
 				"ChannelVarset",
 				"ChannelTalkingStarted",
 				"ChannelTalkingFinished",
+				"ChannelHold",
+				"ChannelUnhold",
+				"ContactStatusChange",
 				"EndpointStateChange",
 				"Dial",
 				"StasisEnd",
 				"StasisStart",
 				"TextMessageReceived",
-				"ChannelConnectedLine"
+				"ChannelConnectedLine",
+				"PeerStatusChange"
 			]
 		},
+		"ContactInfo": {
+			"id": "ContactInfo",
+			"description": "Detailed information about a contact on an endpoint.",
+			"properties": {
+				"uri": {
+					"type": "string",
+					"description": "The location of the contact.",
+					"required": true
+				},
+				"contact_status": {
+					"type": "string",
+					"description": "The current status of the contact.",
+					"required": true,
+					"allowableValues": {
+						"valueType": "LIST",
+						"values": [
+							"Unreachable",
+							"Reachable",
+							"Unknown",
+							"Created",
+							"Removed"
+						]
+					}
+				},
+				"aor": {
+					"type": "string",
+					"description": "The Address of Record this contact belongs to.",
+					"required": true
+				},
+				"roundtrip_usec": {
+					"type": "string",
+					"description": "Current round trip time, in microseconds, for the contact.",
+					"required": false
+				}
+			}
+		},
+		"Peer": {
+			"id": "Peer",
+			"description": "Detailed information about a remote peer that communicates with Asterisk.",
+			"properties": {
+				"peer_status": {
+					"type": "string",
+					"description": "The current state of the peer. Note that the values of the status are dependent on the underlying peer technology.",
+					"required": true
+				},
+				"cause": {
+					"type": "string",
+					"description": "An optional reason associated with the change in peer_status.",
+					"required": false
+				},
+				"address": {
+					"type": "string",
+					"description": "The IP address of the peer.",
+					"required": false
+				},
+				"port": {
+					"type": "string",
+					"description": "The port of the peer.",
+					"required": false
+				},
+				"time": {
+					"type": "string",
+					"description": "The last known time the peer was contacted.",
+					"required": false
+				}
+			}
+		},
 		"DeviceStateChanged": {
 			"id": "DeviceStateChanged",
 			"description": "Notification that a device state has changed.",
@@ -598,6 +677,33 @@
 				}
 			}
 		},
+		"ChannelHold": {
+			"id": "ChannelHold",
+			"description": "A channel initiated a media hold.",
+			"properties": {
+				"channel": {
+					"required": true,
+					"type": "Channel",
+					"description": "The channel that initiated the hold event."
+				},
+				"musicclass": {
+					"required": false,
+					"type": "string",
+					"description": "The music on hold class that the initiator requested."
+				}
+			}
+		},
+		"ChannelUnhold": {
+			"id": "ChannelUnhold",
+			"description": "A channel initiated a media unhold.",
+			"properties": {
+				"channel": {
+					"required": true,
+					"type": "Channel",
+					"description": "The channel that initiated the unhold event."
+				}
+			}
+		},
 		"ChannelTalkingStarted": {
 			"id": "ChannelTalkingStarted",
 			"description": "Talking was detected on the channel.",
@@ -625,6 +731,34 @@
 				}
 			}
 		},
+		"ContactStatusChange": {
+			"id": "ContactStatusChange",
+			"description": "The state of a contact on an endpoint has changed.",
+			"properties": {
+				"endpoint": {
+					"required": true,
+					"type": "Endpoint"
+				},
+				"contact_info": {
+					"required": true,
+					"type": "ContactInfo"
+				}
+			}
+		},
+		"PeerStatusChange": {
+			"id": "PeerStatusChange",
+			"description": "The state of a peer associated with an endpoint has changed.",
+			"properties": {
+				"endpoint": {
+					"required": true,
+					"type": "Endpoint"
+				},
+				"peer": {
+					"required": true,
+					"type": "Peer"
+				}
+			}
+		},
 		"EndpointStateChange": {
 			"id": "EndpointStateChange",
 			"description": "Endpoint state changed.",
diff --git a/rest-api/api-docs/mailboxes.json b/rest-api/api-docs/mailboxes.json
index b348106..5b290fc 100644
--- a/rest-api/api-docs/mailboxes.json
+++ b/rest-api/api-docs/mailboxes.json
@@ -1,8 +1,8 @@
 {
 	"_copyright": "Copyright (C) 2013, Digium, Inc.",
 	"_author": "Jonathan Rose <jrose at digium.com>",
-	"_svn_revision": "$Revision: 429091 $",
-	"apiVersion": "1.6.0",
+	"_svn_revision": "$Revision$",
+	"apiVersion": "1.9.0",
 	"swaggerVersion": "1.1",
 	"basePath": "http://localhost:8088/ari",
 	"resourcePath": "/api-docs/mailboxes.{format}",
diff --git a/rest-api/api-docs/playbacks.json b/rest-api/api-docs/playbacks.json
index 98b511a..e6cb749 100644
--- a/rest-api/api-docs/playbacks.json
+++ b/rest-api/api-docs/playbacks.json
@@ -1,8 +1,8 @@
 {
 	"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
 	"_author": "David M. Lee, II <dlee at digium.com>",
-	"_svn_revision": "$Revision: 429091 $",
-	"apiVersion": "1.6.0",
+	"_svn_revision": "$Revision$",
+	"apiVersion": "1.9.0",
 	"swaggerVersion": "1.1",
 	"basePath": "http://localhost:8088/ari",
 	"resourcePath": "/api-docs/playbacks.{format}",
diff --git a/rest-api/api-docs/recordings.json b/rest-api/api-docs/recordings.json
index a4a096f..bc2b902 100644
--- a/rest-api/api-docs/recordings.json
+++ b/rest-api/api-docs/recordings.json
@@ -1,8 +1,8 @@
 {
 	"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
 	"_author": "David M. Lee, II <dlee at digium.com>",
-	"_svn_revision": "$Revision: 429091 $",
-	"apiVersion": "1.6.0",
+	"_svn_revision": "$Revision$",
+	"apiVersion": "1.9.0",
 	"swaggerVersion": "1.1",
 	"basePath": "http://localhost:8088/ari",
 	"resourcePath": "/api-docs/recordings.{format}",
diff --git a/rest-api/api-docs/sounds.json b/rest-api/api-docs/sounds.json
index 5ed6de7..70d65a4 100644
--- a/rest-api/api-docs/sounds.json
+++ b/rest-api/api-docs/sounds.json
@@ -1,8 +1,8 @@
 {
 	"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
 	"_author": "David M. Lee, II <dlee at digium.com>",
-	"_svn_revision": "$Revision: 429091 $",
-	"apiVersion": "1.6.0",
+	"_svn_revision": "$Revision$",
+	"apiVersion": "1.9.0",
 	"swaggerVersion": "1.1",
 	"basePath": "http://localhost:8088/ari",
 	"resourcePath": "/api-docs/sounds.{format}",
diff --git a/rest-api/resources.json b/rest-api/resources.json
index b33baab..bacbc78 100644
--- a/rest-api/resources.json
+++ b/rest-api/resources.json
@@ -1,8 +1,8 @@
 {
 	"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
 	"_author": "David M. Lee, II <dlee at digium.com>",
-	"_svn_revision": "$Revision: 429091 $",
-	"apiVersion": "1.6.0",
+	"_svn_revision": "$Revision$",
+	"apiVersion": "1.9.0",
 	"swaggerVersion": "1.1",
 	"basePath": "http://localhost:8088/ari",
 	"apis": [
diff --git a/sounds/Makefile b/sounds/Makefile
index 13a7184..a726ece 100644
--- a/sounds/Makefile
+++ b/sounds/Makefile
@@ -19,7 +19,7 @@ CMD_PREFIX?=@
 SOUNDS_DIR:=$(DESTDIR)$(ASTDATADIR)/sounds
 SOUNDS_CACHE_DIR?=
 MOH_DIR:=$(DESTDIR)$(ASTDATADIR)/moh
-CORE_SOUNDS_VERSION:=1.4.26
+CORE_SOUNDS_VERSION:=1.4.27
 EXTRA_SOUNDS_VERSION:=1.4.15
 MOH_VERSION:=2.03
 SOUNDS_URL:=http://downloads.asterisk.org/pub/telephony/sounds/releases
@@ -31,6 +31,7 @@ MCS:=$(subst -ES-,-es-,$(MCS))
 MCS:=$(subst -RU-,-ru-,$(MCS))
 MCS:=$(subst -IT-,-it-,$(MCS))
 MCS:=$(subst -JA-,-ja-,$(MCS))
+MCS:=$(subst -SV-,-sv-,$(MCS))
 MCS:=$(subst -WAV,-wav,$(MCS))
 MCS:=$(subst -ULAW,-ulaw,$(MCS))
 MCS:=$(subst -ALAW,-alaw,$(MCS))
@@ -153,6 +154,8 @@ $(eval $(call sound_format_lang_rule,$(SOUNDS_DIR),core-sounds,ru,$(CORE_SOUNDS_
 
 $(eval $(call sound_format_lang_rule,$(SOUNDS_DIR),core-sounds,ja,$(CORE_SOUNDS_VERSION)))
 
+$(eval $(call sound_format_lang_rule,$(SOUNDS_DIR),core-sounds,sv,$(CORE_SOUNDS_VERSION)))
+
 $(eval $(call sound_format_lang_rule,$(SOUNDS_DIR),extra-sounds,en,$(EXTRA_SOUNDS_VERSION)))
 
 $(eval $(call sound_format_lang_rule,$(SOUNDS_DIR),extra-sounds,en_GB,$(EXTRA_SOUNDS_VERSION)))
diff --git a/sounds/sounds.xml b/sounds/sounds.xml
index 5934bb3..547be4b 100644
--- a/sounds/sounds.xml
+++ b/sounds/sounds.xml
@@ -216,6 +216,33 @@
 		<member name="CORE-SOUNDS-JA-SIREN14" displayname="Japanese, G.722.1C (Siren14) format">
 			<support_level>core</support_level>
 		</member>
+		<member name="CORE-SOUNDS-SV-WAV" displayname="Swedish, WAV format">
+			<support_level>core</support_level>
+		</member>
+		<member name="CORE-SOUNDS-SV-ULAW" displayname="Swedish, mu-Law format">
+			<support_level>core</support_level>
+		</member>
+		<member name="CORE-SOUNDS-SV-ALAW" displayname="Swedish, a-Law format">
+			<support_level>core</support_level>
+		</member>
+		<member name="CORE-SOUNDS-SV-GSM" displayname="Swedish, GSM format">
+			<support_level>core</support_level>
+		</member>
+		<member name="CORE-SOUNDS-SV-G729" displayname="Swedish, G.729 format">
+			<support_level>core</support_level>
+		</member>
+		<member name="CORE-SOUNDS-SV-G722" displayname="Swedish, G.722 format">
+			<support_level>core</support_level>
+		</member>
+		<member name="CORE-SOUNDS-SV-SLN16" displayname="Swedish, Signed-linear 16kHz format">
+			<support_level>core</support_level>
+		</member>
+		<member name="CORE-SOUNDS-SV-SIREN7" displayname="Swedish, G.722.1 (Siren7) format">
+			<support_level>core</support_level>
+		</member>
+		<member name="CORE-SOUNDS-SV-SIREN14" displayname="Swedish, G.722.1C (Siren14) format">
+			<support_level>core</support_level>
+		</member>
 	</category>
 	<category name="MENUSELECT_MOH" displayname="Music On Hold File Packages" positive_output="yes">
 		<member name="MOH-OPSOUND-WAV" displayname="opsound.org Music On Hold Files, WAV format" >
diff --git a/tests/test_abstract_jb.c b/tests/test_abstract_jb.c
index 007cf66..7f16cec 100644
--- a/tests/test_abstract_jb.c
+++ b/tests/test_abstract_jb.c
@@ -36,7 +36,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419175 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/utils.h"
 #include "asterisk/module.h"
diff --git a/tests/test_acl.c b/tests/test_acl.c
index e479f42..c7a711f 100644
--- a/tests/test_acl.c
+++ b/tests/test_acl.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 370453 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/test.h"
 #include "asterisk/acl.h"
@@ -122,6 +122,22 @@ struct acl {
 #define TACL_A AST_SENSE_ALLOW
 #define TACL_D AST_SENSE_DENY
 
+static int build_ha(const struct acl *acl, size_t len, struct ast_ha **ha, const char *acl_name, int *err, struct ast_test *test, enum ast_test_result_state *res) 
+{
+	size_t i;
+
+	for (i = 0; i < len; ++i) {
+		if (!(*ha = ast_append_ha(acl[i].access, acl[i].host, *ha, err))) {
+			ast_test_status_update(test, "Failed to add rule %s with access %s to %s\n",
+					       acl[i].host, acl[i].access, acl_name);
+			*res = AST_TEST_FAIL;
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
 AST_TEST_DEFINE(acl)
 {
 	struct acl permitallv4 = { "0.0.0.0/0", "permit" };
@@ -211,21 +227,6 @@ AST_TEST_DEFINE(acl)
 	int err = 0;
 	int i;
 
-	auto int build_ha(const struct acl *acl, size_t len, struct ast_ha **ha, const char *acl_name);
-	auto int build_ha(const struct acl *acl, size_t len, struct ast_ha **ha, const char *acl_name) {
-		size_t i;
-
-		for (i = 0; i < len; ++i) {
-			if (!(*ha = ast_append_ha(acl[i].access, acl[i].host, *ha, &err))) {
-				ast_test_status_update(test, "Failed to add rule %s with access %s to %s\n",
-						       acl[i].host, acl[i].access, acl_name);
-				res = AST_TEST_FAIL;
-				return -1;
-			}
-		}
-
-		return 0;
-	}
 
 	switch (cmd) {
 	case TEST_INIT:
@@ -263,31 +264,31 @@ AST_TEST_DEFINE(acl)
 		goto acl_cleanup;
 	}
 
-	if (build_ha(acl1, ARRAY_LEN(acl1), &ha1, "ha1") != 0) {
+	if (build_ha(acl1, ARRAY_LEN(acl1), &ha1, "ha1", &err, test, &res) != 0) {
 		goto acl_cleanup;
 	}
 
-	if (build_ha(acl2, ARRAY_LEN(acl2), &ha2, "ha2") != 0) {
+	if (build_ha(acl2, ARRAY_LEN(acl2), &ha2, "ha2", &err, test, &res) != 0) {
 		goto acl_cleanup;
 	}
 
-	if (build_ha(acl3, ARRAY_LEN(acl3), &ha3, "ha3") != 0) {
+	if (build_ha(acl3, ARRAY_LEN(acl3), &ha3, "ha3", &err, test, &res) != 0) {
 		goto acl_cleanup;
 	}
 
-	if (build_ha(acl4, ARRAY_LEN(acl4), &ha4, "ha4") != 0) {
+	if (build_ha(acl4, ARRAY_LEN(acl4), &ha4, "ha4", &err, test, &res) != 0) {
 		goto acl_cleanup;
 	}
 
-	if (build_ha(acl5, ARRAY_LEN(acl5), &ha5, "ha5") != 0) {
+	if (build_ha(acl5, ARRAY_LEN(acl5), &ha5, "ha5", &err, test, &res) != 0) {
 		goto acl_cleanup;
 	}
 
-	if (build_ha(acl6, ARRAY_LEN(acl6), &ha6, "ha6") != 0) {
+	if (build_ha(acl6, ARRAY_LEN(acl6), &ha6, "ha6", &err, test, &res) != 0) {
 		goto acl_cleanup;
 	}
 
-	if (build_ha(acl7, ARRAY_LEN(acl7), &ha7, "ha7") != 0) {
+	if (build_ha(acl7, ARRAY_LEN(acl7), &ha7, "ha7", &err, test, &res) != 0) {
 		goto acl_cleanup;
 	}
 
diff --git a/tests/test_amihooks.c b/tests/test_amihooks.c
index cd65a5a..1f10a01 100644
--- a/tests/test_amihooks.c
+++ b/tests/test_amihooks.c
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 338557 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/cli.h"
diff --git a/tests/test_aoc.c b/tests/test_aoc.c
index 8ec2d93..35387df 100644
--- a/tests/test_aoc.c
+++ b/tests/test_aoc.c
@@ -32,7 +32,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419175 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/utils.h"
 #include "asterisk/module.h"
diff --git a/tests/test_app.c b/tests/test_app.c
index 790b5f0..2d7148a 100644
--- a/tests/test_app.c
+++ b/tests/test_app.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 410158 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/utils.h"
 #include "asterisk/module.h"
diff --git a/tests/test_ari.c b/tests/test_ari.c
index 5ecf53c..fc74544 100644
--- a/tests/test_ari.c
+++ b/tests/test_ari.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 403177 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/test.h"
diff --git a/tests/test_ari_model.c b/tests/test_ari_model.c
index a075cff..12c9a1e 100644
--- a/tests/test_ari_model.c
+++ b/tests/test_ari_model.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 399208 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/utils.h"
 #include "asterisk/module.h"
diff --git a/tests/test_ast_format_str_reduce.c b/tests/test_ast_format_str_reduce.c
index da8a8e2..e0e468c 100644
--- a/tests/test_ast_format_str_reduce.c
+++ b/tests/test_ast_format_str_reduce.c
@@ -41,7 +41,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 332178 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/file.h"
diff --git a/tests/test_astobj2.c b/tests/test_astobj2.c
index 4119848..6484753 100644
--- a/tests/test_astobj2.c
+++ b/tests/test_astobj2.c
@@ -30,7 +30,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419175 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/utils.h"
 #include "asterisk/module.h"
@@ -2026,7 +2026,7 @@ AST_TEST_DEFINE(astobj2_test_perf)
 	switch (cmd) {
 	case TEST_INIT:
 		info->name = "astobj2_test_perf";
-		info->category = "/main/astobj2/perf";
+		info->category = "/main/astobj2/perf/";
 		info->summary = "Test container performance";
 		info->description =
 			"Runs container traversal tests.";
diff --git a/tests/test_astobj2_thrash.c b/tests/test_astobj2_thrash.c
index 1d8aaf6..814234c 100644
--- a/tests/test_astobj2_thrash.c
+++ b/tests/test_astobj2_thrash.c
@@ -35,7 +35,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419175 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include <pthread.h>
 #include "asterisk/astobj2.h"
 #include "asterisk/hashtab.h"
diff --git a/tests/test_callerid.c b/tests/test_callerid.c
index f8f6047..d0788a4 100644
--- a/tests/test_callerid.c
+++ b/tests/test_callerid.c
@@ -34,7 +34,7 @@
 #include "asterisk.h"
 #include "asterisk/callerid.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 425155 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/utils.h"
 #include "asterisk/module.h"
diff --git a/tests/test_cdr.c b/tests/test_cdr.c
index 6bc810c..4a2eede 100644
--- a/tests/test_cdr.c
+++ b/tests/test_cdr.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 423783 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <math.h>
 #include "asterisk/module.h"
@@ -463,7 +463,7 @@ AST_TEST_DEFINE(test_cdr_unanswered_inbound_call)
 		info->description =
 			"Test the properties of a CDR for a call that is\n"
 			"inbound to Asterisk, executes some dialplan, but\n"
-			"is never answered.\n";
+			"is never answered.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -513,7 +513,7 @@ AST_TEST_DEFINE(test_cdr_unanswered_outbound_call)
 		info->summary = "Test outbound unanswered calls";
 		info->description =
 			"Test the properties of a CDR for a call that is\n"
-			"outbound to Asterisk but is never answered.\n";
+			"outbound to Asterisk but is never answered.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -583,7 +583,7 @@ AST_TEST_DEFINE(test_cdr_outbound_bridged_call)
 		info->category = TEST_CATEGORY;
 		info->summary = "Test dialing, answering, and going into a 2-party bridge";
 		info->description =
-			"The most 'basic' of scenarios\n";
+			"The most 'basic' of scenarios";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -662,7 +662,7 @@ AST_TEST_DEFINE(test_cdr_single_party)
 		info->summary = "Test cdrs for a single party";
 		info->description =
 			"Test the properties of a CDR for a call that is\n"
-			"answered, but only involves a single channel\n";
+			"answered, but only involves a single channel";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -713,7 +713,7 @@ AST_TEST_DEFINE(test_cdr_single_bridge)
 		info->summary = "Test cdrs for a single party entering/leaving a bridge";
 		info->description =
 			"Test the properties of a CDR for a call that is\n"
-			"answered, enters a bridge, and leaves it.\n";
+			"answered, enters a bridge, and leaves it.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -788,7 +788,7 @@ AST_TEST_DEFINE(test_cdr_single_bridge_continue)
 		info->summary = "Test cdrs for a single party entering/leaving a bridge";
 		info->description =
 			"Test the properties of a CDR for a call that is\n"
-			"answered, enters a bridge, and leaves it.\n";
+			"answered, enters a bridge, and leaves it.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -872,7 +872,7 @@ AST_TEST_DEFINE(test_cdr_single_twoparty_bridge_a)
 		info->description =
 			"Test the properties of a CDR for a call that is\n"
 			"answered, enters a bridge, and leaves it. In this scenario, the\n"
-			"Party A should answer the bridge first.\n";
+			"Party A should answer the bridge first.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -964,7 +964,7 @@ AST_TEST_DEFINE(test_cdr_single_twoparty_bridge_b)
 		info->description =
 			"Test the properties of a CDR for a call that is\n"
 			"answered, enters a bridge, and leaves it. In this scenario, the\n"
-			"Party B should answer the bridge first.\n";
+			"Party B should answer the bridge first.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -1089,7 +1089,7 @@ AST_TEST_DEFINE(test_cdr_single_multiparty_bridge)
 		info->description =
 			"Test the properties of a CDR for a call that is\n"
 			"answered, enters a bridge, and leaves it. A total of three\n"
-			"parties perform this action.\n";
+			"parties perform this action.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -1179,7 +1179,7 @@ AST_TEST_DEFINE(test_cdr_dial_unanswered)
 		info->summary = "Test CDRs for a dial that isn't answered";
 		info->description =
 			"Test the properties of a CDR for a channel that\n"
-			"performs a dial operation that isn't answered\n";
+			"performs a dial operation that isn't answered";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -1242,7 +1242,7 @@ AST_TEST_DEFINE(test_cdr_dial_busy)
 		info->summary = "Test CDRs for a dial that results in a busy";
 		info->description =
 			"Test the properties of a CDR for a channel that\n"
-			"performs a dial operation to an endpoint that's busy\n";
+			"performs a dial operation to an endpoint that's busy";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -1304,7 +1304,7 @@ AST_TEST_DEFINE(test_cdr_dial_congestion)
 		info->summary = "Test CDRs for a dial that results in congestion";
 		info->description =
 			"Test the properties of a CDR for a channel that\n"
-			"performs a dial operation to an endpoint that's congested\n";
+			"performs a dial operation to an endpoint that's congested";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -1366,7 +1366,7 @@ AST_TEST_DEFINE(test_cdr_dial_unavailable)
 		info->summary = "Test CDRs for a dial that results in unavailable";
 		info->description =
 			"Test the properties of a CDR for a channel that\n"
-			"performs a dial operation to an endpoint that's unavailable\n";
+			"performs a dial operation to an endpoint that's unavailable";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -1429,7 +1429,7 @@ AST_TEST_DEFINE(test_cdr_dial_caller_cancel)
 		info->description =
 			"Test the properties of a CDR for a channel that\n"
 			"performs a dial operation to an endpoint but then decides\n"
-			"to hang up, cancelling the dial\n";
+			"to hang up, cancelling the dial";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -1528,7 +1528,7 @@ AST_TEST_DEFINE(test_cdr_dial_parallel_failed)
 		info->description =
 			"This tests dialing three parties: Bob, Charlie, David. Charlie\n"
 			"returns BUSY; David returns CONGESTION; Bob fails to answer and\n"
-			"Alice hangs up. Three records are created for Alice as a result.\n";
+			"Alice hangs up. Three records are created for Alice as a result.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -1650,7 +1650,7 @@ AST_TEST_DEFINE(test_cdr_dial_answer_no_bridge)
 			"when we answer, we get a CDR, it gets ended at that point, and\n"
 			"that it gets finalized appropriately. We should get three CDRs in\n"
 			"the end - one for the dial, and one for each CDR as they continued\n"
-			"on.\n";
+			"on.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -1720,7 +1720,7 @@ AST_TEST_DEFINE(test_cdr_dial_answer_twoparty_bridge_a)
 		info->category = TEST_CATEGORY;
 		info->summary = "Test dialing, answering, and going into a 2-party bridge";
 		info->description =
-			"The most 'basic' of scenarios\n";
+			"The most 'basic' of scenarios";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -1797,7 +1797,7 @@ AST_TEST_DEFINE(test_cdr_dial_answer_twoparty_bridge_b)
 		info->category = TEST_CATEGORY;
 		info->summary = "Test dialing, answering, and going into a 2-party bridge";
 		info->description =
-			"The most 'basic' of scenarios\n";
+			"The most 'basic' of scenarios";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -1955,7 +1955,7 @@ AST_TEST_DEFINE(test_cdr_dial_answer_multiparty)
 		info->category = TEST_CATEGORY;
 		info->summary = "Test dialing, answering, and going into a multi-party bridge";
 		info->description =
-			"A little tricky to get to do, but possible with some redirects.\n";
+			"A little tricky to get to do, but possible with some redirects.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -2076,7 +2076,7 @@ AST_TEST_DEFINE(test_cdr_park)
 		info->summary = "Test cdrs for a single party entering Park";
 		info->description =
 			"Test the properties of a CDR for calls that are\n"
-			"answered, enters Park, and leaves it.\n";
+			"answered, enters Park, and leaves it.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -2184,7 +2184,7 @@ AST_TEST_DEFINE(test_cdr_fields)
 		info->category = TEST_CATEGORY;
 		info->summary = "Test field access CDRs";
 		info->description =
-			"This tests setting/retrieving data on CDR records.\n";
+			"This tests setting/retrieving data on CDR records.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -2351,7 +2351,7 @@ AST_TEST_DEFINE(test_cdr_no_reset_cdr)
 		info->category = TEST_CATEGORY;
 		info->summary = "Test field access CDRs";
 		info->description =
-			"This tests setting/retrieving data on CDR records.\n";
+			"This tests setting/retrieving data on CDR records.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -2449,7 +2449,7 @@ AST_TEST_DEFINE(test_cdr_fork_cdr)
 		info->category = TEST_CATEGORY;
 		info->summary = "Test field access CDRs";
 		info->description =
-			"This tests setting/retrieving data on CDR records.\n";
+			"This tests setting/retrieving data on CDR records.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
diff --git a/tests/test_cel.c b/tests/test_cel.c
index ad2e358..03e243c 100644
--- a/tests/test_cel.c
+++ b/tests/test_cel.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428919 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <math.h>
 #include "asterisk/module.h"
@@ -430,7 +430,7 @@ AST_TEST_DEFINE(test_cel_unanswered_inbound_call)
 		info->description =
 			"Test CEL records for a call that is\n"
 			"inbound to Asterisk, executes some dialplan, but\n"
-			"is never answered.\n";
+			"is never answered.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -461,7 +461,7 @@ AST_TEST_DEFINE(test_cel_unanswered_outbound_call)
 		info->summary = "Test outbound unanswered calls";
 		info->description =
 			"Test CEL records for a call that is\n"
-			"outbound to Asterisk but is never answered.\n";
+			"outbound to Asterisk but is never answered.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -490,7 +490,7 @@ AST_TEST_DEFINE(test_cel_single_party)
 		info->summary = "Test CEL for a single party";
 		info->description =
 			"Test CEL records for a call that is\n"
-			"answered, but only involves a single channel\n";
+			"answered, but only involves a single channel";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -519,7 +519,7 @@ AST_TEST_DEFINE(test_cel_single_bridge)
 		info->summary = "Test CEL for a single party entering/leaving a bridge";
 		info->description =
 			"Test CEL records for a call that is\n"
-			"answered, enters a bridge, and leaves it.\n";
+			"answered, enters a bridge, and leaves it.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -557,7 +557,7 @@ AST_TEST_DEFINE(test_cel_single_bridge_continue)
 		info->summary = "Test CEL for a single party entering/leaving a bridge";
 		info->description =
 			"Test CEL records for a call that is\n"
-			"answered, enters a bridge, and leaves it.\n";
+			"answered, enters a bridge, and leaves it.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -601,7 +601,7 @@ AST_TEST_DEFINE(test_cel_single_twoparty_bridge_a)
 		info->description =
 			"Test CEL records for a call that is\n"
 			"answered, enters a bridge, and leaves it. In this scenario, the\n"
-			"Party A should answer the bridge first.\n";
+			"Party A should answer the bridge first.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -649,7 +649,7 @@ AST_TEST_DEFINE(test_cel_single_twoparty_bridge_b)
 		info->description =
 			"Test CEL records for a call that is\n"
 			"answered, enters a bridge, and leaves it. In this scenario, the\n"
-			"Party B should answer the bridge first.\n";
+			"Party B should answer the bridge first.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -703,7 +703,7 @@ AST_TEST_DEFINE(test_cel_single_multiparty_bridge)
 		info->description =
 			"Test CEL records for a call that is\n"
 			"answered, enters a bridge, and leaves it. A total of three\n"
-			"parties perform this action.\n";
+			"parties perform this action.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -780,7 +780,7 @@ AST_TEST_DEFINE(test_cel_dial_unanswered)
 		info->summary = "Test CEL for a dial that isn't answered";
 		info->description =
 			"Test CEL records for a channel that\n"
-			"performs a dial operation that isn't answered\n";
+			"performs a dial operation that isn't answered";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -814,7 +814,7 @@ AST_TEST_DEFINE(test_cel_dial_unanswered_filter)
 		info->summary = "Test CEL for a dial that isn't answered";
 		info->description =
 			"Test CEL records for a channel that\n"
-			"performs a dial operation that isn't answered\n";
+			"performs a dial operation that isn't answered";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -849,7 +849,7 @@ AST_TEST_DEFINE(test_cel_dial_busy)
 		info->summary = "Test CEL for a dial that results in a busy";
 		info->description =
 			"Test CEL records for a channel that\n"
-			"performs a dial operation to an endpoint that's busy\n";
+			"performs a dial operation to an endpoint that's busy";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -883,7 +883,7 @@ AST_TEST_DEFINE(test_cel_dial_congestion)
 		info->summary = "Test CEL for a dial that results in congestion";
 		info->description =
 			"Test CEL records for a channel that\n"
-			"performs a dial operation to an endpoint that's congested\n";
+			"performs a dial operation to an endpoint that's congested";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -917,7 +917,7 @@ AST_TEST_DEFINE(test_cel_dial_unavailable)
 		info->summary = "Test CEL for a dial that results in unavailable";
 		info->description =
 			"Test CEL records for a channel that\n"
-			"performs a dial operation to an endpoint that's unavailable\n";
+			"performs a dial operation to an endpoint that's unavailable";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -952,7 +952,7 @@ AST_TEST_DEFINE(test_cel_dial_caller_cancel)
 		info->description =
 			"Test CEL records for a channel that\n"
 			"performs a dial operation to an endpoint but then decides\n"
-			"to hang up, cancelling the dial\n";
+			"to hang up, cancelling the dial";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -989,7 +989,7 @@ AST_TEST_DEFINE(test_cel_dial_parallel_failed)
 		info->description =
 			"This tests dialing three parties: Bob, Charlie, David. Charlie\n"
 			"returns BUSY; David returns CONGESTION; Bob fails to answer and\n"
-			"Alice hangs up. Three records are created for Alice as a result.\n";
+			"Alice hangs up. Three records are created for Alice as a result.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -1042,7 +1042,7 @@ AST_TEST_DEFINE(test_cel_dial_answer_no_bridge)
 			"a dial, then bounce both channels to different priorities and\n"
 			"never have them enter a bridge together. Ew. This makes sure that\n"
 			"when we answer, we get a CEL, it gets ended at that point, and\n"
-			"that it gets finalized appropriately.\n";
+			"that it gets finalized appropriately.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -1083,7 +1083,7 @@ AST_TEST_DEFINE(test_cel_dial_answer_twoparty_bridge_a)
 		info->category = TEST_CATEGORY;
 		info->summary = "Test dialing, answering, and going into a 2-party bridge";
 		info->description =
-			"The most 'basic' of scenarios\n";
+			"The most 'basic' of scenarios";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -1130,7 +1130,7 @@ AST_TEST_DEFINE(test_cel_dial_answer_twoparty_bridge_b)
 		info->category = TEST_CATEGORY;
 		info->summary = "Test dialing, answering, and going into a 2-party bridge";
 		info->description =
-			"The most 'basic' of scenarios\n";
+			"The most 'basic' of scenarios";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -1180,7 +1180,7 @@ AST_TEST_DEFINE(test_cel_dial_answer_multiparty)
 		info->category = TEST_CATEGORY;
 		info->summary = "Test dialing, answering, and going into a multi-party bridge";
 		info->description =
-			"A little tricky to get to do, but possible with some redirects.\n";
+			"A little tricky to get to do, but possible with some redirects.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -1256,7 +1256,7 @@ AST_TEST_DEFINE(test_cel_blind_transfer)
 		info->summary = "Test blind transfers to an extension";
 		info->description =
 			"This test creates two channels, bridges them, and then"
-			" blind transfers the bridge to an extension.\n";
+			" blind transfers the bridge to an extension.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -1327,7 +1327,7 @@ AST_TEST_DEFINE(test_cel_attended_transfer_bridges_swap)
 		info->description =
 			"This test creates four channels, places each pair in"
 			" a bridge, and then attended transfers the bridges"
-			" together.\n";
+			" together.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -1409,7 +1409,7 @@ AST_TEST_DEFINE(test_cel_attended_transfer_bridges_merge)
 		info->description =
 			"This test creates four channels, places each pair"
 			" in a bridge, and then attended transfers the bridges"
-			" together causing a bridge merge.\n";
+			" together causing a bridge merge.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -1497,7 +1497,7 @@ AST_TEST_DEFINE(test_cel_attended_transfer_bridges_link)
 		info->description =
 			"This test creates four channels, places each pair"
 			" in a bridge, and then attended transfers the bridges"
-			" together causing a bridge link.\n";
+			" together causing a bridge link.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -1577,7 +1577,7 @@ AST_TEST_DEFINE(test_cel_dial_pickup)
 		info->description =
 			"Test CEL records for a call that is\n"
 			"inbound to Asterisk, executes some dialplan, and\n"
-			"is picked up.\n";
+			"is picked up.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -1637,7 +1637,7 @@ AST_TEST_DEFINE(test_cel_local_optimize)
 		info->description =
 			"Test CEL records for two local channels being optimized\n"
 			"out by sending a messages indicating local optimization\n"
-			"begin and end\n";
+			"begin and end";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
diff --git a/tests/test_channel_feature_hooks.c b/tests/test_channel_feature_hooks.c
index 92a4fa0..fbc9786 100644
--- a/tests/test_channel_feature_hooks.c
+++ b/tests/test_channel_feature_hooks.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428604 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/test.h"
@@ -169,7 +169,7 @@ AST_TEST_DEFINE(test_features_channel_dtmf)
 		info->description =
 			"This test creates two channels, adds a DTMF hook to one, places them into\n"
 			"a bridge, and verifies that the DTMF hook added to the channel feature\n"
-			"hooks can be triggered once the channel is bridged.\n";
+			"hooks can be triggered once the channel is bridged.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -261,7 +261,7 @@ AST_TEST_DEFINE(test_features_channel_interval)
 		info->description =
 			"This test creates two channels, adds an interval hook to one, places them\n"
 			"into a bridge, and verifies that the interval hook added to the channel\n"
-			"feature hooks is triggered once the channel is bridged.\n";
+			"feature hooks is triggered once the channel is bridged.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
diff --git a/tests/test_config.c b/tests/test_config.c
index 65a11a3..3ad5772 100644
--- a/tests/test_config.c
+++ b/tests/test_config.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 425714 $");
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
 
 #include <math.h> /* HUGE_VAL */
 
@@ -42,6 +42,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 425714 $");
 #include "asterisk/config_options.h"
 #include "asterisk/netsock2.h"
 #include "asterisk/acl.h"
+#include "asterisk/pbx.h"
 #include "asterisk/frame.h"
 #include "asterisk/utils.h"
 #include "asterisk/logger.h"
@@ -233,6 +234,7 @@ AST_TEST_DEFINE(config_basic_ops)
 	struct ast_config *cfg = NULL;
 	struct ast_category *cat = NULL;
 	struct ast_variable *var;
+	struct ast_variable *varlist;
 	char temp[32];
 	const char *cat_name;
 	const char *var_value;
@@ -536,6 +538,22 @@ AST_TEST_DEFINE(config_basic_ops)
 		goto out;
 	}
 
+	varlist = ast_variable_new("name1", "value1", "");
+	ast_variable_list_append_hint(&varlist, NULL, ast_variable_new("name1", "value2", ""));
+	ast_variable_list_append_hint(&varlist, NULL, ast_variable_new("name1", "value3", ""));
+
+	var_value = ast_variable_find_in_list(varlist, "name1");
+	if (strcmp(var_value, "value1") != 0) {
+		ast_test_status_update(test, "Wrong variable retrieved %s.\n", var_value);
+		goto out;
+	}
+
+	var_value = ast_variable_find_last_in_list(varlist, "name1");
+	if (strcmp(var_value, "value3") != 0) {
+		ast_test_status_update(test, "Wrong variable retrieved %s.\n", var_value);
+		goto out;
+	}
+
 	res = AST_TEST_PASS;
 
 out:
@@ -1474,8 +1492,8 @@ AST_TEST_DEFINE(config_options_test)
 			res = AST_TEST_FAIL;
 		}
 		if (!ast_format_cap_identical(arr[x]->codeccapopt, control->codeccapopt)) {
-			struct ast_str *codec_buf1 = ast_str_alloca(64);
-			struct ast_str *codec_buf2 = ast_str_alloca(64);
+			struct ast_str *codec_buf1 = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+			struct ast_str *codec_buf2 = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 			ast_test_status_update(test, "format did not match: '%s' vs '%s' on loop %d\n",
 				ast_format_cap_get_names(arr[x]->codeccapopt, &codec_buf1),
@@ -1504,6 +1522,156 @@ AST_TEST_DEFINE(config_options_test)
 	return res;
 }
 
+AST_TEST_DEFINE(config_dialplan_function)
+{
+	enum ast_test_result_state res = AST_TEST_PASS;
+	FILE *config_file;
+	char filename[PATH_MAX];
+	struct ast_str *buf;
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "config_dialplan_function";
+		info->category = "/main/config/";
+		info->summary = "Test AST_CONFIG dialplan function";
+		info->description = "Test AST_CONFIG dialplan function";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	snprintf(filename, sizeof(filename), "%s/%s",
+			ast_config_AST_CONFIG_DIR, CONFIG_FILE);
+	config_file = fopen(filename, "w");
+
+	if (!config_file) {
+		return AST_TEST_FAIL;
+	}
+
+	fputs(
+		"[c1t](!)\n"
+		"var1=val1\n"
+		"var1=val2\n"
+		"var2=val21\n"
+		"\n"
+		"[c1](c1t)\n"
+		"var1=val3\n"
+		"var1=val4\n"
+		, config_file);
+
+	fclose(config_file);
+
+	if (!(buf = ast_str_create(32))) {
+		ast_test_status_update(test, "Failed to allocate return buffer\n");
+		res = AST_TEST_FAIL;
+		goto out;
+	}
+
+	if (ast_func_read2(NULL, "AST_CONFIG("CONFIG_FILE",c1,var1)", &buf, 32)) {
+		ast_test_status_update(test, "Failed to retrieve field 'var1'\n");
+		res = AST_TEST_FAIL;
+		goto out;
+	}
+	if (strcmp(ast_str_buffer(buf), "val1")) {
+		ast_test_status_update(test, "Got '%s', should be '%s'\n",
+			ast_str_buffer(buf), "val1");
+		res = AST_TEST_FAIL;
+		goto out;
+	}
+
+	ast_str_reset(buf);
+	if (ast_func_read2(NULL, "AST_CONFIG("CONFIG_FILE",c1,var1,0)", &buf, 32)) {
+		ast_test_status_update(test, "Failed to retrieve field 'var1'\n");
+		res = AST_TEST_FAIL;
+		goto out;
+	}
+	if (strcmp(ast_str_buffer(buf), "val1")) {
+		ast_test_status_update(test, "Got '%s', should be '%s'\n",
+			ast_str_buffer(buf), "val1");
+		res = AST_TEST_FAIL;
+		goto out;
+	}
+
+	ast_str_reset(buf);
+	if (ast_func_read2(NULL, "AST_CONFIG("CONFIG_FILE",c1,var1,1)", &buf, 32)) {
+		ast_test_status_update(test, "Failed to retrieve field 'var1'\n");
+		res = AST_TEST_FAIL;
+		goto out;
+	}
+	if (strcmp(ast_str_buffer(buf), "val2")) {
+		ast_test_status_update(test, "Got '%s', should be '%s'\n",
+			ast_str_buffer(buf), "val2");
+		res = AST_TEST_FAIL;
+		goto out;
+	}
+
+	ast_str_reset(buf);
+	if (ast_func_read2(NULL, "AST_CONFIG("CONFIG_FILE",c1,var1,2)", &buf, 32)) {
+		ast_test_status_update(test, "Failed to retrieve field 'var1'\n");
+		res = AST_TEST_FAIL;
+		goto out;
+	}
+	if (strcmp(ast_str_buffer(buf), "val3")) {
+		ast_test_status_update(test, "Got '%s', should be '%s'\n",
+			ast_str_buffer(buf), "val3");
+		res = AST_TEST_FAIL;
+		goto out;
+	}
+
+	ast_str_reset(buf);
+	if (ast_func_read2(NULL, "AST_CONFIG("CONFIG_FILE",c1,var1,3)", &buf, 32)) {
+		ast_test_status_update(test, "Failed to retrieve field 'var1'\n");
+		res = AST_TEST_FAIL;
+		goto out;
+	}
+	if (strcmp(ast_str_buffer(buf), "val4")) {
+		ast_test_status_update(test, "Got '%s', should be '%s'\n",
+			ast_str_buffer(buf), "val4");
+		res = AST_TEST_FAIL;
+		goto out;
+	}
+
+	ast_str_reset(buf);
+	if (ast_func_read2(NULL, "AST_CONFIG("CONFIG_FILE",c1,var1,-1)", &buf, 32)) {
+		ast_test_status_update(test, "Failed to retrieve field 'var1'\n");
+		res = AST_TEST_FAIL;
+		goto out;
+	}
+	if (strcmp(ast_str_buffer(buf), "val4")) {
+		ast_test_status_update(test, "Got '%s', should be '%s'\n",
+			ast_str_buffer(buf), "val4");
+		res = AST_TEST_FAIL;
+		goto out;
+	}
+
+	ast_str_reset(buf);
+	if (ast_func_read2(NULL, "AST_CONFIG("CONFIG_FILE",c1,var2,-1)", &buf, 32)) {
+		ast_test_status_update(test, "Failed to retrieve field 'var2'\n");
+		res = AST_TEST_FAIL;
+		goto out;
+	}
+	if (strcmp(ast_str_buffer(buf), "val21")) {
+		ast_test_status_update(test, "Got '%s', should be '%s'\n",
+			ast_str_buffer(buf), "val21");
+		res = AST_TEST_FAIL;
+		goto out;
+	}
+
+	ast_str_reset(buf);
+	if (!ast_func_read2(NULL, "AST_CONFIG("CONFIG_FILE",c1,var1,5)", &buf, 32)) {
+		ast_test_status_update(test, "Should not have retrieved a value\n");
+		res = AST_TEST_FAIL;
+		goto out;
+	}
+
+out:
+	if (buf) {
+		ast_free(buf);
+	}
+	delete_config_file();
+	return res;
+}
+
 static int unload_module(void)
 {
 	AST_TEST_UNREGISTER(config_basic_ops);
@@ -1513,6 +1681,7 @@ static int unload_module(void)
 	AST_TEST_UNREGISTER(config_hook);
 	AST_TEST_UNREGISTER(ast_parse_arg_test);
 	AST_TEST_UNREGISTER(config_options_test);
+	AST_TEST_UNREGISTER(config_dialplan_function);
 	return 0;
 }
 
@@ -1525,6 +1694,7 @@ static int load_module(void)
 	AST_TEST_REGISTER(config_hook);
 	AST_TEST_REGISTER(ast_parse_arg_test);
 	AST_TEST_REGISTER(config_options_test);
+	AST_TEST_REGISTER(config_dialplan_function);
 	return AST_MODULE_LOAD_SUCCESS;
 }
 
diff --git a/tests/test_core_codec.c b/tests/test_core_codec.c
index fe8acff..499e633 100644
--- a/tests/test_core_codec.c
+++ b/tests/test_core_codec.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419044 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/test.h"
 #include "asterisk/module.h"
diff --git a/tests/test_core_format.c b/tests/test_core_format.c
index 9f9c463..a3819c6 100644
--- a/tests/test_core_format.c
+++ b/tests/test_core_format.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419110 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/test.h"
 #include "asterisk/module.h"
@@ -45,6 +45,7 @@ static int test_core_format_clone(const struct ast_format *src, struct ast_forma
 static enum ast_format_cmp_res test_core_format_cmp(const struct ast_format *format1, const struct ast_format *format2);
 static struct ast_format *test_core_format_get_joint(const struct ast_format *format1, const struct ast_format *format2);
 static struct ast_format *test_core_format_attribute_set(const struct ast_format *format, const char *name, const char *value);
+static const void *test_core_format_attribute_get(const struct ast_format *format, const char *name);
 static struct ast_format *test_core_format_parse_sdp_fmtp(const struct ast_format *format, const char *attributes);
 static void test_core_format_generate_sdp_fmtp(const struct ast_format *format, unsigned int payload, struct ast_str **str);
 
@@ -55,6 +56,7 @@ static struct ast_format_interface test_core_format_attr = {
 	.format_cmp = &test_core_format_cmp,
 	.format_get_joint = &test_core_format_get_joint,
 	.format_attribute_set = &test_core_format_attribute_set,
+	.format_attribute_get = &test_core_format_attribute_get,
 	.format_parse_sdp_fmtp = &test_core_format_parse_sdp_fmtp,
 	.format_generate_sdp_fmtp = &test_core_format_generate_sdp_fmtp,
 };
@@ -202,6 +204,19 @@ static struct ast_format *test_core_format_attribute_set(const struct ast_format
 	return clone;
 }
 
+/*! \brief Format attribute callback for retrieving an attribute */
+static const void *test_core_format_attribute_get(const struct ast_format *format, const char *name)
+{
+	struct test_core_format_pvt *pvt = ast_format_get_attribute_data(format);
+
+	if (!strcmp(name, "one")) {
+		return &pvt->field_one;
+	} else if (!strcmp(name, "two")) {
+		return &pvt->field_two;
+	}
+	return NULL;
+}
+
 /*! \brief Format attribute callback to construct a format from an SDP fmtp line */
 static struct ast_format *test_core_format_parse_sdp_fmtp(const struct ast_format *format, const char *attributes)
 {
@@ -333,6 +348,55 @@ AST_TEST_DEFINE(format_create_attr)
 	return AST_TEST_PASS;
 }
 
+AST_TEST_DEFINE(format_retrieve_attr)
+{
+	RAII_VAR(struct ast_codec *, codec, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_format *, format, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_format *, format_w_attr, NULL, ao2_cleanup);
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = __PRETTY_FUNCTION__;
+		info->category = TEST_CATEGORY;
+		info->summary = "Format attribute retrieval unit test";
+		info->description =
+			"Test retrieval of format attributes";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	codec = ast_codec_get("test_core_format_codec", AST_MEDIA_TYPE_AUDIO, 8000);
+	if (!codec) {
+		ast_test_status_update(test, "Could not retrieve test_core_format_codec codec\n");
+		return AST_TEST_FAIL;
+	}
+
+	format = ast_format_create(codec);
+	if (!format) {
+		ast_test_status_update(test, "Could not create format using test_core_format_codec codec\n");
+		return AST_TEST_FAIL;
+	}
+
+	format_w_attr = ast_format_attribute_set(format, "one", "1");
+	if (!format_w_attr) {
+		ast_test_status_update(test, "Could not create format with attributes using test_core_format_codec codec\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (*((int *)ast_format_attribute_get(format_w_attr, "one")) != 1) {
+		ast_test_status_update(test, "Could not retrieve valid format attribute\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (ast_format_attribute_get(format_w_attr, "foo") != NULL) {
+		ast_test_status_update(test, "Retrieved invalid format attribute\n");
+		return AST_TEST_FAIL;
+	}
+
+	return AST_TEST_PASS;
+}
+
 AST_TEST_DEFINE(format_clone)
 {
 	RAII_VAR(struct ast_codec *, codec, NULL, ao2_cleanup);
@@ -829,6 +893,43 @@ AST_TEST_DEFINE(format_attribute_set_without_interface)
 	return AST_TEST_PASS;
 }
 
+AST_TEST_DEFINE(format_attribute_get_without_interface)
+{
+	RAII_VAR(struct ast_codec *, codec, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_format *, format, NULL, ao2_cleanup);
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = __PRETTY_FUNCTION__;
+		info->category = TEST_CATEGORY;
+		info->summary = "Format attribute retrieval unit test";
+		info->description =
+			"Test that attribute retrieval on a format without an interface fails";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	codec = ast_codec_get("ulaw", AST_MEDIA_TYPE_AUDIO, 8000);
+	if (!codec) {
+		ast_test_status_update(test, "Could not retrieve built-in ulaw codec\n");
+		return AST_TEST_FAIL;
+	}
+
+	format = ast_format_create(codec);
+	if (!format) {
+		ast_test_status_update(test, "Could not create format using built-in codec\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (ast_format_attribute_get(format, "bees") != NULL) {
+		ast_test_status_update(test, "Successfully retrieved an attribute on a format without an interface\n");
+		return AST_TEST_FAIL;
+	}
+
+	return AST_TEST_PASS;
+}
+
 AST_TEST_DEFINE(format_parse_sdp_fmtp_without_interface)
 {
 	RAII_VAR(struct ast_codec *, codec, NULL, ao2_cleanup);
@@ -925,6 +1026,7 @@ static int unload_module(void)
 {
 	AST_TEST_UNREGISTER(format_create);
 	AST_TEST_UNREGISTER(format_create_attr);
+	AST_TEST_UNREGISTER(format_retrieve_attr);
 	AST_TEST_UNREGISTER(format_clone);
 	AST_TEST_UNREGISTER(format_cmp_same_codec);
 	AST_TEST_UNREGISTER(format_attr_cmp_same_codec);
@@ -934,6 +1036,7 @@ static int unload_module(void)
 	AST_TEST_UNREGISTER(format_joint_different_codec);
 	AST_TEST_UNREGISTER(format_copy);
 	AST_TEST_UNREGISTER(format_attribute_set_without_interface);
+	AST_TEST_UNREGISTER(format_attribute_get_without_interface);
 	AST_TEST_UNREGISTER(format_parse_sdp_fmtp_without_interface);
 	AST_TEST_UNREGISTER(format_parse_and_generate_sdp_fmtp);
 
@@ -955,6 +1058,7 @@ static int load_module(void)
 
 	AST_TEST_REGISTER(format_create);
 	AST_TEST_REGISTER(format_create_attr);
+	AST_TEST_REGISTER(format_retrieve_attr);
 	AST_TEST_REGISTER(format_clone);
 	AST_TEST_REGISTER(format_cmp_same_codec);
 	AST_TEST_REGISTER(format_attr_cmp_same_codec);
@@ -964,6 +1068,7 @@ static int load_module(void)
 	AST_TEST_REGISTER(format_joint_different_codec);
 	AST_TEST_REGISTER(format_copy);
 	AST_TEST_REGISTER(format_attribute_set_without_interface);
+	AST_TEST_REGISTER(format_attribute_get_without_interface);
 	AST_TEST_REGISTER(format_parse_sdp_fmtp_without_interface);
 	AST_TEST_REGISTER(format_parse_and_generate_sdp_fmtp);
 
diff --git a/tests/test_devicestate.c b/tests/test_devicestate.c
index 85255ec..5f2cddf 100644
--- a/tests/test_devicestate.c
+++ b/tests/test_devicestate.c
@@ -32,7 +32,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 410185 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/utils.h"
 #include "asterisk/module.h"
@@ -40,9 +40,21 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 410185 $")
 #include "asterisk/devicestate.h"
 #include "asterisk/pbx.h"
 #include "asterisk/stasis_message_router.h"
+#include "asterisk/vector.h"
 
 #define UNIT_TEST_DEVICE_IDENTIFIER "unit_test_device_identifier"
 
+#define DEVICE_STATE_CHANNEL_TYPE "TestDeviceState"
+
+#define DEVSTATE_PROVIDER "TestDevState"
+
+#define DEVSTATE_PROVIDER_LC "testdevstate"
+
+#define DEVSTATE_PROVIDER_LEN 12
+
+/*! \brief Used to assign an increasing integer to channel name */
+static unsigned int chan_idx;
+
 /* These arrays are the result of the 'core show device2extenstate' output. */
 static int combined_results[] = {
 	AST_DEVICE_UNKNOWN,
@@ -212,6 +224,93 @@ static int exten_results[] = {
 	AST_EXTENSION_ONHOLD,
 };
 
+/*! \brief Mutex for \c update_cond */
+AST_MUTEX_DEFINE_STATIC(update_lock);
+
+/*! \brief Condition wait variable for device state updates */
+static ast_cond_t update_cond;
+
+/*! \brief Mutext for \c channel_cb_cond */
+AST_MUTEX_DEFINE_STATIC(channel_cb_lock);
+
+/*! \brief Condition wait variable for channel tech device state cb */
+static ast_cond_t channel_cb_cond;
+
+/*! \brief The resulting device state updates caused by some function call */
+static AST_VECTOR(, enum ast_device_state) result_states;
+
+/*! \brief The current device state for our device state provider */
+static enum ast_device_state current_device_state;
+
+/*! \brief Clear out all recorded device states in \ref result_states */
+static void clear_result_states(void)
+{
+	ast_mutex_lock(&update_lock);
+	while (AST_VECTOR_SIZE(&result_states) > 0) {
+		AST_VECTOR_REMOVE_UNORDERED(&result_states, 0);
+	}
+	ast_mutex_unlock(&update_lock);
+}
+
+/*! \brief Stasis subscription callback for device state updates */
+static void device_state_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message)
+{
+	struct ast_device_state_message *payload;
+	enum ast_device_state state;
+	const char *device;
+
+	if (stasis_message_type(message) != ast_device_state_message_type()) {
+		return;
+	}
+
+	payload = stasis_message_data(message);
+	state = payload->state;
+	device = payload->device;
+
+	if (ast_strlen_zero(device)) {
+		return;
+	}
+
+	/* Ignore aggregate events */
+	if (!payload->eid) {
+		return;
+	}
+
+	if (strncasecmp(device, DEVSTATE_PROVIDER, DEVSTATE_PROVIDER_LEN)) {
+		return;
+	}
+
+	ast_mutex_lock(&update_lock);
+	AST_VECTOR_APPEND(&result_states, state);
+	ast_cond_signal(&update_cond);
+	ast_mutex_unlock(&update_lock);
+}
+
+static enum ast_device_state devstate_prov_cb(const char *data)
+{
+	return current_device_state;
+}
+
+static int wait_for_device_state_updates(struct ast_test *test, int expected_updates)
+{
+	int error;
+	struct timeval wait_now = ast_tvnow();
+	struct timespec wait_time = { .tv_sec = wait_now.tv_sec + 1, .tv_nsec = wait_now.tv_usec * 1000 };
+
+	ast_mutex_lock(&update_lock);
+	while (AST_VECTOR_SIZE(&result_states) != expected_updates) {
+		error = ast_cond_timedwait(&update_cond, &update_lock, &wait_time);
+		if (error == ETIMEDOUT) {
+			ast_test_status_update(test, "Test timed out while waiting for %d expected updates\n", expected_updates);
+			break;
+		}
+	}
+	ast_mutex_unlock(&update_lock);
+
+	ast_test_status_update(test, "Received %zu of %d updates\n", AST_VECTOR_SIZE(&result_states), expected_updates);
+	return !(AST_VECTOR_SIZE(&result_states) == expected_updates);
+}
+
 AST_TEST_DEFINE(device2extenstate_test)
 {
 	int res = AST_TEST_PASS;
@@ -522,17 +621,350 @@ AST_TEST_DEFINE(device_state_aggregation_test)
 	return AST_TEST_PASS;
 }
 
+AST_TEST_DEFINE(devstate_prov_add)
+{
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = __func__;
+		info->category = "/main/devicestate/";
+		info->summary = "Test adding a device state provider";
+		info->description =
+			"Test that a custom device state provider can be added, and that\n"
+			"it cannot be added if already added.";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	ast_test_validate(test, ast_devstate_prov_add(DEVSTATE_PROVIDER, devstate_prov_cb) == 0);
+	ast_test_validate(test, ast_devstate_prov_add(DEVSTATE_PROVIDER, devstate_prov_cb) != 0);
+	ast_test_validate(test, ast_devstate_prov_del(DEVSTATE_PROVIDER) == 0);
+
+	return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(devstate_prov_del)
+{
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = __func__;
+		info->category = "/main/devicestate/";
+		info->summary = "Test removing a device state provider";
+		info->description =
+			"Test that a custom device state provider can be removed, and that\n"
+			"it cannot be removed if already removed.";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	ast_test_validate(test, ast_devstate_prov_add(DEVSTATE_PROVIDER, devstate_prov_cb) == 0);
+	ast_test_validate(test, ast_devstate_prov_del(DEVSTATE_PROVIDER) == 0);
+	ast_test_validate(test, ast_devstate_prov_del(DEVSTATE_PROVIDER) != 0);
+
+	return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(devstate_changed)
+{
+	RAII_VAR(struct stasis_subscription *, sub, NULL, ao2_cleanup);
+	int i;
+	enum ast_device_state expected_results[] = {
+		AST_DEVICE_NOT_INUSE,
+		AST_DEVICE_INUSE,
+		AST_DEVICE_BUSY,
+		AST_DEVICE_INVALID,
+		AST_DEVICE_UNAVAILABLE,
+		AST_DEVICE_RINGING,
+		AST_DEVICE_RINGINUSE,
+		AST_DEVICE_ONHOLD,
+	};
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = __func__;
+		info->category = "/main/devicestate/";
+		info->summary = "Test updates coming from a device state provider";
+		info->description =
+			"This unit test checks that a custom device state provider can\n"
+			"have updates published for it. This includes both cacheable and\n"
+			"non-cacheable events. In the case of non-cacheable events, the\n"
+			"device state provider's callback function is queried for the\n"
+			"device state when AST_DEVICE_UNKNOWN is published.";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	ast_test_validate(test, (sub = stasis_subscribe(ast_device_state_topic_all(), device_state_cb, NULL)) != NULL);
+
+	clear_result_states();
+	current_device_state = AST_DEVICE_BUSY;
+
+	ast_test_validate(test, ast_devstate_prov_add(DEVSTATE_PROVIDER, devstate_prov_cb) == 0);
+
+	ast_test_validate(test, ast_devstate_changed_literal(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_NOT_CACHABLE, DEVSTATE_PROVIDER ":foo") == 0);
+	ast_test_validate(test, ast_devstate_changed_literal(AST_DEVICE_INUSE, AST_DEVSTATE_NOT_CACHABLE, DEVSTATE_PROVIDER ":foo") == 0);
+	ast_test_validate(test, ast_devstate_changed_literal(AST_DEVICE_BUSY, AST_DEVSTATE_NOT_CACHABLE, DEVSTATE_PROVIDER ":foo") == 0);
+	ast_test_validate(test, ast_devstate_changed_literal(AST_DEVICE_INVALID, AST_DEVSTATE_NOT_CACHABLE, DEVSTATE_PROVIDER ":foo") == 0);
+	ast_test_validate(test, ast_devstate_changed_literal(AST_DEVICE_UNAVAILABLE, AST_DEVSTATE_NOT_CACHABLE, DEVSTATE_PROVIDER ":foo") == 0);
+	ast_test_validate(test, ast_devstate_changed_literal(AST_DEVICE_RINGING, AST_DEVSTATE_NOT_CACHABLE, DEVSTATE_PROVIDER ":foo") == 0);
+	ast_test_validate(test, ast_devstate_changed_literal(AST_DEVICE_RINGINUSE, AST_DEVSTATE_NOT_CACHABLE, DEVSTATE_PROVIDER ":foo") == 0);
+	ast_test_validate(test, ast_devstate_changed_literal(AST_DEVICE_ONHOLD, AST_DEVSTATE_NOT_CACHABLE, DEVSTATE_PROVIDER ":foo") == 0);
+
+	ast_test_validate(test, wait_for_device_state_updates(test, 8) == 0);
+
+	for (i = 0; i < AST_VECTOR_SIZE(&result_states); i++) {
+		ast_test_status_update(test, "Testing update %d: actual is %d; expected is %d\n",
+			i,
+			AST_VECTOR_GET(&result_states, i),
+			expected_results[i]);
+		ast_test_validate(test, AST_VECTOR_GET(&result_states, i) == expected_results[i]);
+	}
+
+	sub = stasis_unsubscribe_and_join(sub);
+	clear_result_states();
+
+	/*
+	 * Since an update of AST_DEVICE_UNKNOWN will cause a different thread to retrieve
+	 * the update from the custom device state provider, check it separately from the
+	 * updates above.
+	 */
+	ast_test_validate(test, (sub = stasis_subscribe(ast_device_state_topic_all(), device_state_cb, NULL)) != NULL);
+	ast_test_validate(test, ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, AST_DEVSTATE_NOT_CACHABLE, DEVSTATE_PROVIDER ":foo") == 0);
+	ast_test_validate(test, wait_for_device_state_updates(test, 1) == 0);
+
+	ast_test_validate(test, AST_VECTOR_GET(&result_states, 0) == AST_DEVICE_BUSY);
+	ast_test_validate(test, ast_device_state(DEVSTATE_PROVIDER ":foo") == AST_DEVICE_BUSY);
+	ast_test_validate(test, ast_device_state(DEVSTATE_PROVIDER_LC ":foo") == AST_DEVICE_BUSY);
+
+	sub = stasis_unsubscribe_and_join(sub);
+	clear_result_states();
+
+	ast_test_validate(test, (sub = stasis_subscribe(ast_device_state_topic_all(), device_state_cb, NULL)) != NULL);
+	ast_test_validate(test, ast_devstate_changed_literal(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, DEVSTATE_PROVIDER ":foo") == 0);
+	ast_test_validate(test, ast_devstate_changed_literal(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, DEVSTATE_PROVIDER ":foo") == 0);
+	ast_test_validate(test, ast_devstate_changed_literal(AST_DEVICE_BUSY, AST_DEVSTATE_CACHABLE, DEVSTATE_PROVIDER ":foo") == 0);
+	ast_test_validate(test, ast_devstate_changed_literal(AST_DEVICE_INVALID, AST_DEVSTATE_CACHABLE, DEVSTATE_PROVIDER ":foo") == 0);
+	ast_test_validate(test, ast_devstate_changed_literal(AST_DEVICE_UNAVAILABLE, AST_DEVSTATE_CACHABLE, DEVSTATE_PROVIDER ":foo") == 0);
+	ast_test_validate(test, ast_devstate_changed_literal(AST_DEVICE_RINGING, AST_DEVSTATE_CACHABLE, DEVSTATE_PROVIDER ":foo") == 0);
+	ast_test_validate(test, ast_devstate_changed_literal(AST_DEVICE_RINGINUSE, AST_DEVSTATE_CACHABLE, DEVSTATE_PROVIDER ":foo") == 0);
+	ast_test_validate(test, ast_devstate_changed_literal(AST_DEVICE_ONHOLD, AST_DEVSTATE_CACHABLE, DEVSTATE_PROVIDER ":foo") == 0);
+
+	ast_test_validate(test, wait_for_device_state_updates(test, 8) == 0);
+	for (i = 0; i < AST_VECTOR_SIZE(&result_states); i++) {
+		ast_test_status_update(test, "Testing update %d: actual is %d; expected is %d\n",
+			i,
+			AST_VECTOR_GET(&result_states, i),
+			expected_results[i]);
+		ast_test_validate(test, AST_VECTOR_GET(&result_states, i) == expected_results[i]);
+	}
+
+	/*
+	 * Check the last value in the cache. Note that this should not hit
+	 * the value of current_device_state.
+	 */
+	ast_test_validate(test, ast_device_state(DEVSTATE_PROVIDER ":foo") == AST_DEVICE_ONHOLD);
+	/*
+	 * This will miss on the cache, as it is case sensitive. It should go
+	 * hit our device state callback however.
+	 */
+	ast_test_validate(test, ast_device_state(DEVSTATE_PROVIDER_LC ":foo") == AST_DEVICE_BUSY);
+
+	/* Generally, this test can't be run twice in a row, as you can't remove an
+	 * item from the cache. Hence, subsequent runs won't hit the device state provider,
+	 * and will merely return the cached value.
+	 *
+	 * To avoid annoying errors, set the last state to BUSY here.
+	 */
+	ast_devstate_changed_literal(AST_DEVICE_BUSY, AST_DEVSTATE_CACHABLE, DEVSTATE_PROVIDER ":foo");
+
+	ast_test_validate(test, ast_devstate_prov_del(DEVSTATE_PROVIDER) == 0);
+
+	sub = stasis_unsubscribe_and_join(sub);
+	clear_result_states();
+
+	return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(devstate_conversions)
+{
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = __func__;
+		info->category = "/main/devicestate/";
+		info->summary = "Test ast_device_state conversions";
+		info->description =
+			"Test various transformations of ast_device_state values.";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	ast_test_validate(test, !strcmp(ast_devstate_str(AST_DEVICE_UNKNOWN), "UNKNOWN"));
+	ast_test_validate(test, !strcmp(ast_devstate_str(AST_DEVICE_NOT_INUSE), "NOT_INUSE"));
+	ast_test_validate(test, !strcmp(ast_devstate_str(AST_DEVICE_INUSE), "INUSE"));
+	ast_test_validate(test, !strcmp(ast_devstate_str(AST_DEVICE_BUSY), "BUSY"));
+	ast_test_validate(test, !strcmp(ast_devstate_str(AST_DEVICE_INVALID), "INVALID"));
+	ast_test_validate(test, !strcmp(ast_devstate_str(AST_DEVICE_UNAVAILABLE), "UNAVAILABLE"));
+	ast_test_validate(test, !strcmp(ast_devstate_str(AST_DEVICE_RINGING), "RINGING"));
+	ast_test_validate(test, !strcmp(ast_devstate_str(AST_DEVICE_RINGINUSE), "RINGINUSE"));
+	ast_test_validate(test, !strcmp(ast_devstate_str(AST_DEVICE_ONHOLD), "ONHOLD"));
+
+	ast_test_validate(test, ast_devstate_val("UNKNOWN") == AST_DEVICE_UNKNOWN);
+	ast_test_validate(test, ast_devstate_val("NOT_INUSE") == AST_DEVICE_NOT_INUSE);
+	ast_test_validate(test, ast_devstate_val("INUSE") == AST_DEVICE_INUSE);
+	ast_test_validate(test, ast_devstate_val("BUSY") == AST_DEVICE_BUSY);
+	ast_test_validate(test, ast_devstate_val("INVALID") == AST_DEVICE_INVALID);
+	ast_test_validate(test, ast_devstate_val("UNAVAILABLE") == AST_DEVICE_UNAVAILABLE);
+	ast_test_validate(test, ast_devstate_val("RINGING") == AST_DEVICE_RINGING);
+	ast_test_validate(test, ast_devstate_val("RINGINUSE") == AST_DEVICE_RINGINUSE);
+	ast_test_validate(test, ast_devstate_val("ONHOLD") == AST_DEVICE_ONHOLD);
+	ast_test_validate(test, ast_devstate_val("onhold") == AST_DEVICE_ONHOLD);
+	ast_test_validate(test, ast_devstate_val("FOO") == AST_DEVICE_UNKNOWN);
+
+	ast_test_validate(test, ast_state_chan2dev(AST_STATE_DOWN) == AST_DEVICE_NOT_INUSE);
+	ast_test_validate(test, ast_state_chan2dev(AST_STATE_RESERVED) == AST_DEVICE_INUSE);
+	ast_test_validate(test, ast_state_chan2dev(AST_STATE_OFFHOOK) == AST_DEVICE_INUSE);
+	ast_test_validate(test, ast_state_chan2dev(AST_STATE_DIALING) == AST_DEVICE_INUSE);
+	ast_test_validate(test, ast_state_chan2dev(AST_STATE_RING) == AST_DEVICE_INUSE);
+	ast_test_validate(test, ast_state_chan2dev(AST_STATE_RINGING) == AST_DEVICE_RINGING);
+	ast_test_validate(test, ast_state_chan2dev(AST_STATE_UP) == AST_DEVICE_INUSE);
+	ast_test_validate(test, ast_state_chan2dev(AST_STATE_BUSY) == AST_DEVICE_BUSY);
+	ast_test_validate(test, ast_state_chan2dev(AST_STATE_DIALING_OFFHOOK) == AST_DEVICE_INUSE);
+	ast_test_validate(test, ast_state_chan2dev(AST_STATE_PRERING) == AST_DEVICE_RINGING);
+
+	return AST_TEST_PASS;
+}
+
+/*! \brief Whether or not the channel device state callback was called */
+static int chan_callback_called;
+
+/*! \brief Wait until the test channel driver's devicestate callback is called */
+static int wait_for_channel_callback(struct ast_test *test)
+{
+	int error;
+	struct timeval wait_now = ast_tvnow();
+	struct timespec wait_time = { .tv_sec = wait_now.tv_sec + 1, .tv_nsec = wait_now.tv_usec * 1000 };
+
+	ast_mutex_lock(&channel_cb_lock);
+	while (!chan_callback_called) {
+		error = ast_cond_timedwait(&channel_cb_cond, &channel_cb_lock, &wait_time);
+		if (error == ETIMEDOUT) {
+			ast_test_status_update(test, "Test timed out while waiting channel callback\n");
+			break;
+		}
+	}
+	ast_mutex_unlock(&channel_cb_lock);
+
+	return chan_callback_called;
+}
+
+static void safe_hangup(void *object)
+{
+	struct ast_channel *chan = object;
+
+	if (!chan) {
+		return;
+	}
+	ast_hangup(chan);
+}
+
+AST_TEST_DEFINE(devstate_channels)
+{
+	RAII_VAR(struct ast_channel *, chan, NULL, safe_hangup);
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = __func__;
+		info->category = "/main/devicestate/";
+		info->summary = "Test deriving device state from a channel's state";
+		info->description =
+			"Test querying a channel's state to derive a device state.";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	chan_callback_called = 0;
+
+	chan = ast_channel_alloc(0, AST_STATE_RINGING, "", "", "", "s", "default",
+		NULL, NULL, 0, DEVICE_STATE_CHANNEL_TYPE "/foo-%08x",
+		(unsigned) ast_atomic_fetchadd_int((int *) &chan_idx, +1));
+	ast_test_validate(test, chan != NULL);
+
+	ast_test_validate(test, ast_parse_device_state(DEVICE_STATE_CHANNEL_TYPE "/foo") == AST_DEVICE_RINGING);
+	ast_test_validate(test, ast_parse_device_state(DEVICE_STATE_CHANNEL_TYPE "/bad") == AST_DEVICE_UNKNOWN);
+
+	ast_setstate(chan, AST_STATE_UP);
+
+	ast_test_validate(test, wait_for_channel_callback(test) == 1);
+	ast_test_validate(test, ast_parse_device_state(DEVICE_STATE_CHANNEL_TYPE "/foo") == AST_DEVICE_INUSE);
+
+	chan_callback_called = 0;
+
+	return AST_TEST_PASS;
+}
+
+static int chan_test_devicestate_cb(const char *device_number)
+{
+	/* Simply record that we were called when expected */
+	chan_callback_called = 1;
+
+	ast_mutex_lock(&channel_cb_lock);
+	ast_cond_signal(&channel_cb_cond);
+	ast_mutex_unlock(&channel_cb_lock);
+
+	return AST_DEVICE_INUSE;
+}
+
+struct ast_channel_tech chan_test_devicestate = {
+	.type = DEVICE_STATE_CHANNEL_TYPE,
+	.description = "Device State Unit Test Channel Driver",
+	.devicestate = chan_test_devicestate_cb,
+};
+
 static int unload_module(void)
 {
+	AST_VECTOR_FREE(&result_states);
+	ast_channel_unregister(&chan_test_devicestate);
+
 	AST_TEST_UNREGISTER(device2extenstate_test);
 	AST_TEST_UNREGISTER(device_state_aggregation_test);
+
+	AST_TEST_UNREGISTER(devstate_prov_add);
+	AST_TEST_UNREGISTER(devstate_prov_del);
+
+	AST_TEST_UNREGISTER(devstate_changed);
+	AST_TEST_UNREGISTER(devstate_conversions);
+
+	AST_TEST_UNREGISTER(devstate_channels);
+
 	return 0;
 }
 
 static int load_module(void)
 {
+	if (AST_VECTOR_INIT(&result_states, 8) == -1) {
+		return AST_MODULE_LOAD_DECLINE;
+	}
+
+	if (ast_channel_register(&chan_test_devicestate)) {
+		AST_VECTOR_FREE(&result_states);
+		return AST_MODULE_LOAD_DECLINE;
+	}
+
 	AST_TEST_REGISTER(device_state_aggregation_test);
 	AST_TEST_REGISTER(device2extenstate_test);
+
+	AST_TEST_REGISTER(devstate_prov_add);
+	AST_TEST_REGISTER(devstate_prov_del);
+
+	AST_TEST_REGISTER(devstate_changed);
+	AST_TEST_REGISTER(devstate_conversions);
+
+	AST_TEST_REGISTER(devstate_channels);
+
 	return AST_MODULE_LOAD_SUCCESS;
 }
 
diff --git a/tests/test_dlinklists.c b/tests/test_dlinklists.c
index d351404..197dd75 100644
--- a/tests/test_dlinklists.c
+++ b/tests/test_dlinklists.c
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 401663 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/file.h"
 #include "asterisk/channel.h"
diff --git a/tests/test_endpoints.c b/tests/test_endpoints.c
index 120fc4e..4447861 100644
--- a/tests/test_endpoints.c
+++ b/tests/test_endpoints.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 392779 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/astobj2.h"
 #include "asterisk/endpoints.h"
diff --git a/tests/test_event.c b/tests/test_event.c
index 6060923..ab6eab1 100644
--- a/tests/test_event.c
+++ b/tests/test_event.c
@@ -41,7 +41,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419175 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/utils.h"
diff --git a/tests/test_expr.c b/tests/test_expr.c
index 0a99afc..2939c20 100644
--- a/tests/test_expr.c
+++ b/tests/test_expr.c
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 385718 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/utils.h"
 #include "asterisk/module.h"
@@ -158,7 +158,7 @@ AST_TEST_DEFINE(expr_test)
 		info->category = "/main/ast_expr/";
 		info->summary = "unit test for the internal expression engine";
 		info->description =
-			"Verifies behavior for the internal expression engine\n";
+			"Verifies behavior for the internal expression engine";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
diff --git a/tests/test_format_cache.c b/tests/test_format_cache.c
index 0e48046..cc1696b 100644
--- a/tests/test_format_cache.c
+++ b/tests/test_format_cache.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419044 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/test.h"
 #include "asterisk/module.h"
diff --git a/tests/test_format_cap.c b/tests/test_format_cap.c
index c5d131e..ed5427f 100644
--- a/tests/test_format_cap.c
+++ b/tests/test_format_cap.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419044 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/test.h"
 #include "asterisk/module.h"
@@ -245,7 +245,7 @@ AST_TEST_DEFINE(format_cap_append_all_unknown)
 	} else if (!ast_format_cap_has_type(caps, AST_MEDIA_TYPE_VIDEO)) {
 		ast_test_status_update(test, "Added all media formats but no video formats exist when they should\n");
 		return AST_TEST_FAIL;
-	} else if ((ast_format_cap_count(caps) + 1) != ast_codec_get_max()) {
+	} else if ((ast_format_cap_count(caps) + 1) != (ast_codec_get_max() - 1)) {
 		ast_test_status_update(test, "The number of formats in the capabilities structure does not match known number\n");
 		return AST_TEST_FAIL;
 	}
@@ -1079,7 +1079,7 @@ AST_TEST_DEFINE(format_cap_get_names)
 	RAII_VAR(struct ast_format *, ulaw_format, NULL, ao2_cleanup);
 	RAII_VAR(struct ast_codec *, alaw, NULL, ao2_cleanup);
 	RAII_VAR(struct ast_format *, alaw_format, NULL, ao2_cleanup);
-	struct ast_str *buffer = ast_str_alloca(128);
+	struct ast_str *buffer = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
 	switch (cmd) {
 	case TEST_INIT:
@@ -1088,7 +1088,7 @@ AST_TEST_DEFINE(format_cap_get_names)
 		info->summary = "Test getting the names of formats";
 		info->description =
 			"Test that obtaining the names from a format capabilities structure\n"
-			"produces the expected output.\n";
+			"produces the expected output.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
diff --git a/tests/test_func_file.c b/tests/test_func_file.c
index 287e747..8349075 100644
--- a/tests/test_func_file.c
+++ b/tests/test_func_file.c
@@ -32,7 +32,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 385718 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/utils.h"
 #include "asterisk/app.h"
@@ -63,6 +63,12 @@ static struct {
 	/* No length */
 	{ "123456789", "-5", "56789" },
 	{ "123456789", "4", "56789" },
+	/* Passed file length */
+	{ "123456789", "8,10", "9" },
+	{ "123456789", "10,1", "" },
+	/* Middle of file */
+	{ "123456789", "2,5", "34567" },
+	{ "123456789", "-7,5", "34567" },
 	/* Line mode, 4 ways of specifying the first character */
 	{ "123\n456\n789\n", "0,1,l", "123\n" },
 	{ "123\n456\n789\n", "-3,1,l", "123\n" },
diff --git a/tests/test_gosub.c b/tests/test_gosub.c
index 6fd9cca..05a3550 100644
--- a/tests/test_gosub.c
+++ b/tests/test_gosub.c
@@ -32,7 +32,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 389251 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/utils.h"
 #include "asterisk/module.h"
@@ -42,10 +42,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 389251 $")
 
 AST_TEST_DEFINE(test_gosub)
 {
+#define CONTEXT_NAME "tests_test_gosub_virtual_context"
 	int res = AST_TEST_PASS, i;
 	struct ast_channel *chan;
 	struct ast_str *str;
-	struct ast_context *con;
 	struct testplan {
 		const char *app;
 		const char *args;
@@ -119,14 +119,14 @@ AST_TEST_DEFINE(test_gosub)
 	}
 
 	/* Create our test dialplan */
-	if (!(con = ast_context_find_or_create(NULL, NULL, "tests_test_gosub_virtual_context", "test_gosub"))) {
+	if (!ast_context_find_or_create(NULL, NULL, CONTEXT_NAME, "test_gosub")) {
 		ast_test_status_update(test, "Unable to create test dialplan context");
 		ast_free(str);
 		ast_channel_unref(chan);
 		return AST_TEST_FAIL;
 	}
 
-	ast_add_extension2(con, 1, "s", 1, NULL, NULL, "NoOp", ast_strdup(""), ast_free_ptr, "test_gosub");
+	ast_add_extension(CONTEXT_NAME, 1, "s", 1, NULL, NULL, "NoOp", ast_strdup(""), ast_free_ptr, "test_gosub");
 
 	for (i = 0; i < ARRAY_LEN(testplan); i++) {
 		if (testplan[i].app == NULL) {
@@ -157,8 +157,8 @@ AST_TEST_DEFINE(test_gosub)
 
 	ast_free(str);
 	ast_channel_unref(chan);
-	ast_context_remove_extension2(con, "s", 1, NULL, 0);
-	ast_context_destroy(con, "test_gosub");
+	ast_context_remove_extension(CONTEXT_NAME, "s", 1, NULL);
+	ast_context_destroy(NULL, "test_gosub");
 
 	return res;
 }
diff --git a/tests/test_hashtab_thrash.c b/tests/test_hashtab_thrash.c
index 9942d1c..7379b95 100644
--- a/tests/test_hashtab_thrash.c
+++ b/tests/test_hashtab_thrash.c
@@ -35,7 +35,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419175 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include <pthread.h>
 #include "asterisk/hashtab.h"
 #include "asterisk/lock.h"
diff --git a/tests/test_heap.c b/tests/test_heap.c
index d10da1b..cf1a914 100644
--- a/tests/test_heap.c
+++ b/tests/test_heap.c
@@ -30,7 +30,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 332178 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/utils.h"
diff --git a/tests/test_jitterbuf.c b/tests/test_jitterbuf.c
index d29e89d..58dd46d 100644
--- a/tests/test_jitterbuf.c
+++ b/tests/test_jitterbuf.c
@@ -32,7 +32,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 396857 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/utils.h"
 #include "asterisk/module.h"
diff --git a/tests/test_json.c b/tests/test_json.c
index e4f6c2f..9d624cd 100644
--- a/tests/test_json.c
+++ b/tests/test_json.c
@@ -36,7 +36,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419175 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/json.h"
 #include "asterisk/module.h"
 #include "asterisk/test.h"
diff --git a/tests/test_linkedlists.c b/tests/test_linkedlists.c
index e8b5a85..9edab94 100644
--- a/tests/test_linkedlists.c
+++ b/tests/test_linkedlists.c
@@ -30,7 +30,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 401793 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/test.h"
diff --git a/tests/test_locale.c b/tests/test_locale.c
index 4eb4bf1..de14a08 100644
--- a/tests/test_locale.c
+++ b/tests/test_locale.c
@@ -32,7 +32,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 385718 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/types.h>
 #include <dirent.h>
diff --git a/tests/test_logger.c b/tests/test_logger.c
index e1afe9e..b0df4c6 100644
--- a/tests/test_logger.c
+++ b/tests/test_logger.c
@@ -32,7 +32,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419175 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/file.h"
 #include "asterisk/channel.h"
diff --git a/tests/test_message.c b/tests/test_message.c
index 37aec88..f7ee027 100644
--- a/tests/test_message.c
+++ b/tests/test_message.c
@@ -32,7 +32,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420098 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <regex.h>
 
@@ -51,8 +51,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420098 $")
 /*! \brief The number of user events we should get in a dialplan test */
 #define DEFAULT_EXPECTED_EVENTS 4
 
-static struct ast_context *test_message_context;
-
 /*! \brief The current number of received user events */
 static int received_user_events;
 
@@ -160,7 +158,7 @@ static int verify_user_event_fields(int user_event, const char *header, const ch
 				bad_headers_head = AST_VECTOR_GET(&bad_headers, user_event);
 			}
 			ast_variable_list_append(&bad_headers_head, bad_header);
-			AST_VECTOR_INSERT(&bad_headers, user_event, bad_headers_head);
+			AST_VECTOR_REPLACE(&bad_headers, user_event, bad_headers_head);
 		}
 		regfree(&regexbuf);
 		return -1;
@@ -312,7 +310,7 @@ AST_TEST_DEFINE(test_message_msg_tech_registration)
 		info->description =
 			"Test that:\n"
 			"\tA message technology can be registered once only\n"
-			"\tA registered message technology can be unregistered once only\n";
+			"\tA registered message technology can be unregistered once only";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -345,7 +343,7 @@ AST_TEST_DEFINE(test_message_msg_handler_registration)
 		info->description =
 			"Test that:\n"
 			"\tA message handler can be registered once only\n"
-			"\tA registered message handler can be unregistered once only\n";
+			"\tA registered message handler can be unregistered once only";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -393,7 +391,7 @@ AST_TEST_DEFINE(test_message_manipulation)
 			"This test covers the following:\n"
 			"\tSetting/getting the body\n"
 			"\tSetting/getting inbound/outbound variables\n"
-			"\tIterating over variables\n";
+			"\tIterating over variables";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -479,7 +477,7 @@ AST_TEST_DEFINE(test_message_queue_dialplan_nominal)
 		info->summary = "Test enqueueing messages to the dialplan";
 		info->description =
 			"Test that a message enqueued for the dialplan is\n"
-			"passed to that particular extension\n";
+			"passed to that particular extension";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -492,28 +490,28 @@ AST_TEST_DEFINE(test_message_queue_dialplan_nominal)
 	ast_variable_list_append(&expected_response, expected);
 	expected = ast_variable_new("Value","^foo$", __FILE__);
 	ast_variable_list_append(&expected_response, expected);
-	AST_VECTOR_INSERT(&expected_user_event_fields, 0, expected_response);
+	AST_VECTOR_REPLACE(&expected_user_event_fields, 0, expected_response);
 
 	expected_response = NULL;
 	expected = ast_variable_new("Verify", "^From$", __FILE__);
 	ast_variable_list_append(&expected_response, expected);
 	expected = ast_variable_new("Value","^bar$", __FILE__);
 	ast_variable_list_append(&expected_response, expected);
-	AST_VECTOR_INSERT(&expected_user_event_fields, 1, expected_response);
+	AST_VECTOR_REPLACE(&expected_user_event_fields, 1, expected_response);
 
 	expected_response = NULL;
 	expected = ast_variable_new("Verify", "^Body$", __FILE__);
 	ast_variable_list_append(&expected_response, expected);
 	expected = ast_variable_new("Value", "^a body$", __FILE__);
 	ast_variable_list_append(&expected_response, expected);
-	AST_VECTOR_INSERT(&expected_user_event_fields, 2, expected_response);
+	AST_VECTOR_REPLACE(&expected_user_event_fields, 2, expected_response);
 
 	expected_response = NULL;
 	expected = ast_variable_new("Verify", "^Custom$", __FILE__);
 	ast_variable_list_append(&expected_response, expected);
 	expected = ast_variable_new("Value", "^field$", __FILE__);
 	ast_variable_list_append(&expected_response, expected);
-	AST_VECTOR_INSERT(&expected_user_event_fields, 3, expected_response);
+	AST_VECTOR_REPLACE(&expected_user_event_fields, 3, expected_response);
 
 	ast_msg_set_to(msg, "foo");
 	ast_msg_set_from(msg, "bar");
@@ -550,7 +548,7 @@ AST_TEST_DEFINE(test_message_queue_handler_nominal)
 		info->summary = "Test enqueueing messages to a handler";
 		info->description =
 			"Test that a message enqueued can be handled by a\n"
-			"non-dialplan handler\n";
+			"non-dialplan handler";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -593,7 +591,7 @@ AST_TEST_DEFINE(test_message_queue_both_nominal)
 		info->description =
 			"Test that a message enqueued is passed to all\n"
 			"handlers that can process it, dialplan as well as\n"
-			"a custom handler\n";
+			"a custom handler";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -609,21 +607,21 @@ AST_TEST_DEFINE(test_message_queue_both_nominal)
 	ast_variable_list_append(&expected_response, expected);
 	expected = ast_variable_new("Value","^foo$", __FILE__);
 	ast_variable_list_append(&expected_response, expected);
-	AST_VECTOR_INSERT(&expected_user_event_fields, 0, expected_response);
+	AST_VECTOR_REPLACE(&expected_user_event_fields, 0, expected_response);
 
 	expected_response = NULL;
 	expected = ast_variable_new("Verify", "^From$", __FILE__);
 	ast_variable_list_append(&expected_response, expected);
 	expected = ast_variable_new("Value","^bar$", __FILE__);
 	ast_variable_list_append(&expected_response, expected);
-	AST_VECTOR_INSERT(&expected_user_event_fields, 1, expected_response);
+	AST_VECTOR_REPLACE(&expected_user_event_fields, 1, expected_response);
 
 	expected_response = NULL;
 	expected = ast_variable_new("Verify", "^Body$", __FILE__);
 	ast_variable_list_append(&expected_response, expected);
 	expected = ast_variable_new("Value", "^a body$", __FILE__);
 	ast_variable_list_append(&expected_response, expected);
-	AST_VECTOR_INSERT(&expected_user_event_fields, 2, expected_response);
+	AST_VECTOR_REPLACE(&expected_user_event_fields, 2, expected_response);
 
 	ast_msg_set_to(msg, "foo");
 	ast_msg_set_from(msg, "bar");
@@ -664,7 +662,7 @@ AST_TEST_DEFINE(test_message_has_destination_dialplan)
 		info->summary = "Test checking for a dialplan destination";
 		info->description =
 			"Test that a message's destination is verified via the\n"
-			"dialplan\n";
+			"dialplan";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -706,7 +704,7 @@ AST_TEST_DEFINE(test_message_has_destination_handler)
 		info->summary = "Test checking for a handler destination";
 		info->description =
 			"Test that a message's destination is verified via a\n"
-			"handler\n";
+			"handler";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -746,7 +744,7 @@ AST_TEST_DEFINE(test_message_msg_send)
 		info->summary = "Test message routing";
 		info->description =
 			"Test that a message can be routed if it has\n"
-			"a valid handler\n";
+			"a valid handler";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -822,9 +820,7 @@ static int unload_module(void)
 	AST_TEST_UNREGISTER(test_message_has_destination_handler);
 	AST_TEST_UNREGISTER(test_message_msg_send);
 
-	if (test_message_context) {
-		ast_context_destroy(test_message_context, AST_MODULE);
-	}
+	ast_context_destroy(NULL, AST_MODULE);
 
 	ast_manager_unregister_hook(&user_event_hook);
 
@@ -835,8 +831,7 @@ static int create_test_dialplan(void)
 {
 	int res = 0;
 
-	test_message_context = ast_context_find_or_create(NULL, NULL, TEST_CONTEXT, AST_MODULE);
-	if (!test_message_context) {
+	if (!ast_context_find_or_create(NULL, NULL, TEST_CONTEXT, AST_MODULE)) {
 		return -1;
 	}
 
diff --git a/tests/test_optional_api.c b/tests/test_optional_api.c
index 243d57c..6a7d394 100644
--- a/tests/test_optional_api.c
+++ b/tests/test_optional_api.c
@@ -35,7 +35,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419175 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/optional_api.h"
diff --git a/tests/test_pbx.c b/tests/test_pbx.c
index 35329cf..bb5d8e8 100644
--- a/tests/test_pbx.c
+++ b/tests/test_pbx.c
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 337063 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/pbx.h"
@@ -198,7 +198,6 @@ AST_TEST_DEFINE(pattern_match_test)
 	 */
 	struct {
 		const char * context_string;
-		struct ast_context *context;
 	} contexts[] = {
 		{ TEST_PATTERN, },
 		{ TEST_PATTERN_INCLUDE, },
@@ -267,7 +266,7 @@ AST_TEST_DEFINE(pattern_match_test)
 	 */
 
 	for (i = 0; i < ARRAY_LEN(contexts); ++i) {
-		if (!(contexts[i].context = ast_context_find_or_create(NULL, NULL, contexts[i].context_string, registrar))) {
+		if (!ast_context_find_or_create(NULL, NULL, contexts[i].context_string, registrar)) {
 			ast_test_status_update(test, "Failed to create context %s\n", contexts[i].context_string);
 			res = AST_TEST_FAIL;
 			goto cleanup;
@@ -319,11 +318,7 @@ AST_TEST_DEFINE(pattern_match_test)
 	}
 
 cleanup:
-	for (i = 0; i < ARRAY_LEN(contexts); ++i) {
-		if (contexts[i].context) {
-			ast_context_destroy(contexts[i].context, registrar);
-		}
-	}
+	ast_context_destroy(NULL, registrar);
 
 	return res;
 }
diff --git a/tests/test_poll.c b/tests/test_poll.c
index b5e9aa2..8acb717 100644
--- a/tests/test_poll.c
+++ b/tests/test_poll.c
@@ -39,7 +39,7 @@
 #include <errno.h>
 #include <unistd.h>
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 385718 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/utils.h"
 #include "asterisk/module.h"
@@ -88,7 +88,7 @@ AST_TEST_DEFINE(poll_test)
 		info->category = "/main/poll/";
 		info->summary = "unit test for the ast_poll() API";
 		info->description =
-			"Verifies behavior for the ast_poll() API call\n";
+			"Verifies behavior for the ast_poll() API call";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
diff --git a/tests/test_res_stasis.c b/tests/test_res_stasis.c
index 9150558..5865f09 100644
--- a/tests/test_res_stasis.c
+++ b/tests/test_res_stasis.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 393529 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/test.h"
diff --git a/tests/test_sched.c b/tests/test_sched.c
index 5eccd64..5ad2f5d 100644
--- a/tests/test_sched.c
+++ b/tests/test_sched.c
@@ -32,7 +32,7 @@
 
 #include <inttypes.h>
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 332178 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/utils.h"
@@ -202,7 +202,7 @@ static char *handle_cli_sched_bench(struct ast_cli_entry *e, int cmd, struct ast
 	start = ast_tvnow();
 
 	for (i = 0; i < num; i++) {
-		int when = abs(ast_random()) % 60000;
+		long when = labs(ast_random()) % 60000;
 		if ((sched_ids[i] = ast_sched_add(con, when, sched_cb, NULL)) == -1) {
 			ast_cli(a->fd, "Test failed - sched_add returned -1\n");
 			goto return_cleanup;
diff --git a/tests/test_security_events.c b/tests/test_security_events.c
index 59009de..72d6fb5 100644
--- a/tests/test_security_events.c
+++ b/tests/test_security_events.c
@@ -30,7 +30,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 388975 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/module.h"
 #include "asterisk/cli.h"
diff --git a/tests/test_skel.c b/tests/test_skel.c
index a1e2a86..122003d 100644
--- a/tests/test_skel.c
+++ b/tests/test_skel.c
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 332178 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/utils.h"
 #include "asterisk/module.h"
diff --git a/tests/test_sorcery.c b/tests/test_sorcery.c
index 9d32e3b..521d6e8 100644
--- a/tests/test_sorcery.c
+++ b/tests/test_sorcery.c
@@ -179,6 +179,19 @@ static struct sorcery_test_caching cache = { 0, };
 /*! \brief Global scope observer structure for testing */
 static struct sorcery_test_observer observer;
 
+static void *wizard2_data;
+
+static void *sorcery_test_open(const char *data)
+{
+	wizard2_data = (void *)data;
+	return wizard2_data;
+}
+
+static void sorcery_test_close(void *data)
+{
+
+}
+
 static int sorcery_test_create(const struct ast_sorcery *sorcery, void *data, void *object)
 {
 	cache.created = 1;
@@ -204,7 +217,7 @@ static int sorcery_test_delete(const struct ast_sorcery *sorcery, void *data, vo
 	return 0;
 }
 
-/*! \brief Dummy sorcery wizard, not actually used so we only populate the name and nothing else */
+/*! \brief Dummy sorcery wizards, not actually used so we only populate the name and nothing else */
 static struct ast_sorcery_wizard test_wizard = {
 	.name = "test",
 	.create = sorcery_test_create,
@@ -213,6 +226,16 @@ static struct ast_sorcery_wizard test_wizard = {
 	.delete = sorcery_test_delete,
 };
 
+static struct ast_sorcery_wizard test_wizard2 = {
+	.name = "test2",
+	.open = sorcery_test_open,
+	.close = sorcery_test_close,
+	.create = sorcery_test_create,
+	.retrieve_id = sorcery_test_retrieve_id,
+	.update = sorcery_test_update,
+	.delete = sorcery_test_delete,
+};
+
 static void sorcery_observer_created(const void *object)
 {
 	SCOPED_MUTEX(lock, &observer.lock);
@@ -3340,6 +3363,111 @@ AST_TEST_DEFINE(wizard_observation)
 	return AST_TEST_PASS;
 }
 
+AST_TEST_DEFINE(wizard_apply_and_insert)
+{
+	RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
+	RAII_VAR(struct ast_sorcery_wizard *, wizard1, &test_wizard, ast_sorcery_wizard_unregister);
+	RAII_VAR(struct ast_sorcery_wizard *, wizard2, &test_wizard2, ast_sorcery_wizard_unregister);
+	RAII_VAR(struct ast_sorcery_wizard *, wizard, NULL, ao2_cleanup);
+	void *data;
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "wizard_apply_and_insert";
+		info->category = "/main/sorcery/";
+		info->summary = "sorcery wizard apply and insert test";
+		info->description =
+			"sorcery wizard apply and insert test";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	wizard1->load = sorcery_test_load;
+	wizard1->reload = sorcery_test_load;
+
+	wizard2->load = sorcery_test_load;
+	wizard2->reload = sorcery_test_load;
+
+	if (!(sorcery = ast_sorcery_open())) {
+		ast_test_status_update(test, "Failed to open a sorcery instance\n");
+		return AST_TEST_FAIL;
+	}
+
+	ast_sorcery_wizard_register(wizard1);
+	ast_sorcery_wizard_register(wizard2);
+
+	/* test_object_type isn't registered yet so count should return error */
+	ast_test_validate(test,
+		ast_sorcery_get_wizard_mapping_count(sorcery, "test_object_type") == -1);
+
+	ast_sorcery_apply_default(sorcery, "test_object_type", "test", NULL);
+
+	ast_test_validate(test,
+		ast_sorcery_get_wizard_mapping_count(sorcery, "test_object_type") == 1);
+
+	ast_test_validate(test,
+		ast_sorcery_get_wizard_mapping(sorcery, "test_object_type", 0, &wizard, NULL) == 0);
+	ast_test_validate(test, strcmp("test", wizard->name) == 0);
+	ao2_ref(wizard, -1);
+	wizard = NULL;
+
+	ast_test_validate(test,
+		ast_sorcery_insert_wizard_mapping(sorcery, "test_object_type", "test2", "test2data", 0, 0) == 0);
+
+	ast_test_validate(test,
+		ast_sorcery_insert_wizard_mapping(sorcery, "test_object_type", "test2", "test2data", 0, 0) != 0);
+
+	ast_test_validate(test,
+		ast_sorcery_get_wizard_mapping(sorcery, "test_object_type", 0, &wizard, &data) == 0);
+	ast_test_validate(test, strcmp("test2", wizard->name) == 0);
+	ast_test_validate(test, strcmp("test2data", data) == 0);
+	ao2_ref(wizard, -1);
+	wizard = NULL;
+
+	ast_test_validate(test,
+		ast_sorcery_get_wizard_mapping(sorcery, "test_object_type", 1, &wizard, NULL) == 0);
+	ast_test_validate(test, strcmp("test", wizard->name) == 0);
+	ao2_ref(wizard, -1);
+	wizard = NULL;
+
+	/* Test failures */
+	ast_test_validate(test,
+		ast_sorcery_get_wizard_mapping(sorcery, "non-existent-type", 0, &wizard, NULL) != 0);
+
+	ast_test_validate(test,
+		ast_sorcery_get_wizard_mapping(sorcery, "test_object_type", -1, &wizard, &data) != 0);
+
+	ast_test_validate(test,
+		ast_sorcery_get_wizard_mapping(sorcery, "test_object_type", 2, &wizard, NULL) != 0);
+
+	ast_test_validate(test,
+		ast_sorcery_get_wizard_mapping(sorcery, "test_object_type", 2, NULL, NULL) != 0);
+
+	/* Test remove */
+	/* Should fail */
+	ast_test_validate(test,
+		ast_sorcery_remove_wizard_mapping(sorcery, "non-existent-type", "somewizard") != 0);
+	ast_test_validate(test,
+		ast_sorcery_remove_wizard_mapping(sorcery, "test_object_type", "somewizard") != 0);
+
+	/* should work */
+	ast_test_validate(test,
+		ast_sorcery_remove_wizard_mapping(sorcery, "test_object_type", "test") == 0);
+
+	ast_test_validate(test,
+		ast_sorcery_get_wizard_mapping_count(sorcery, "test_object_type") == 1);
+
+	ast_test_validate(test,
+		ast_sorcery_get_wizard_mapping(sorcery, "test_object_type", 0, &wizard, &data) == 0);
+	ast_test_validate(test, strcmp("test2", wizard->name) == 0);
+	ast_test_validate(test, strcmp("test2data", data) == 0);
+	ao2_ref(wizard, -1);
+	wizard = NULL;
+
+	return AST_TEST_PASS;
+}
+
 static int unload_module(void)
 {
 	AST_TEST_UNREGISTER(wizard_registration);
@@ -3390,12 +3518,14 @@ static int unload_module(void)
 	AST_TEST_UNREGISTER(global_observation);
 	AST_TEST_UNREGISTER(instance_observation);
 	AST_TEST_UNREGISTER(wizard_observation);
+	AST_TEST_UNREGISTER(wizard_apply_and_insert);
 
 	return 0;
 }
 
 static int load_module(void)
 {
+	AST_TEST_REGISTER(wizard_apply_and_insert);
 	AST_TEST_REGISTER(wizard_registration);
 	AST_TEST_REGISTER(sorcery_open);
 	AST_TEST_REGISTER(apply_default);
diff --git a/tests/test_sorcery_memory_cache_thrash.c b/tests/test_sorcery_memory_cache_thrash.c
new file mode 100644
index 0000000..2d18317
--- /dev/null
+++ b/tests/test_sorcery_memory_cache_thrash.c
@@ -0,0 +1,618 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2015, Digium, Inc.
+ *
+ * Joshua Colp <jcolp 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 Sorcery Unit Tests
+ *
+ * \author Joshua Colp <jcolp at digium.com>
+ *
+ */
+
+/*** MODULEINFO
+	<depend>TEST_FRAMEWORK</depend>
+	<support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+#include "asterisk/test.h"
+#include "asterisk/module.h"
+#include "asterisk/sorcery.h"
+#include "asterisk/logger.h"
+#include "asterisk/vector.h"
+#include "asterisk/cli.h"
+
+/*! \brief The default amount of time (in seconds) that thrash unit tests execute for */
+#define TEST_THRASH_TIME 3
+
+/*! \brief The number of threads to use for retrieving for applicable tests */
+#define TEST_THRASH_RETRIEVERS 25
+
+/*! \brief The number of threads to use for updating for applicable tests*/
+#define TEST_THRASH_UPDATERS 25
+
+/*! \brief Structure for a memory cache thras thread */
+struct sorcery_memory_cache_thrash_thread {
+	/*! \brief The thread thrashing the cache */
+	pthread_t thread;
+	/*! \brief Sorcery instance being tested */
+	struct ast_sorcery *sorcery;
+	/*! \brief The number of unique objects we should restrict ourself to */
+	unsigned int unique_objects;
+	/*! \brief Set when the thread should stop */
+	unsigned int stop;
+	/*! \brief Average time spent executing sorcery operation in this thread */
+	unsigned int average_execution_time;
+};
+
+/*! \brief Structure for memory cache thrasing */
+struct sorcery_memory_cache_thrash {
+	/*! \brief The sorcery instance being tested */
+	struct ast_sorcery *sorcery;
+	/*! \brief The number of threads which are updating */
+	unsigned int update_threads;
+	/*! \brief The average execution time of sorcery update operations */
+	unsigned int average_update_execution_time;
+	/*! \brief The number of threads which are retrieving */
+	unsigned int retrieve_threads;
+	/*! \brief The average execution time of sorcery retrieve operations */
+	unsigned int average_retrieve_execution_time;
+	/*! \brief Threads which are updating or reading from the cache */
+	AST_VECTOR(, struct sorcery_memory_cache_thrash_thread *) threads;
+};
+
+/*!
+ * \brief Sorcery object created based on backend data
+ */
+struct test_data {
+	SORCERY_OBJECT(details);
+};
+
+/*!
+ * \brief Allocation callback for test_data sorcery object
+ */
+static void *test_data_alloc(const char *id)
+{
+	return ast_sorcery_generic_alloc(sizeof(struct test_data), NULL);
+}
+
+/*!
+ * \brief Callback for retrieving sorcery object by ID
+ *
+ * \param sorcery The sorcery instance
+ * \param data Unused
+ * \param type The object type. Will always be "test".
+ * \param id The object id. Will always be "test".
+ *
+ * \retval NULL Backend data successfully allocated
+ * \retval non-NULL Backend data could not be successfully allocated
+ */
+static void *mock_retrieve_id(const struct ast_sorcery *sorcery, void *data,
+		const char *type, const char *id)
+{
+	return ast_sorcery_alloc(sorcery, type, id);
+}
+
+/*!
+ * \brief Callback for updating a sorcery object
+ *
+ * \param sorcery The sorcery instance
+ * \param data Unused
+ * \param object The object to update.
+ *
+ */
+static int mock_update(const struct ast_sorcery *sorcery, void *data,
+	void *object)
+{
+	return 0;
+}
+
+/*!
+ * \brief A mock sorcery wizard used for the stale test
+ */
+static struct ast_sorcery_wizard mock_wizard = {
+	.name = "mock",
+	.retrieve_id = mock_retrieve_id,
+	.update = mock_update,
+};
+
+/*!
+ * \internal
+ * \brief Destructor for sorcery memory cache thrasher
+ *
+ * \param obj The sorcery memory cache thrash structure
+ */
+static void sorcery_memory_cache_thrash_destroy(void *obj)
+{
+	struct sorcery_memory_cache_thrash *thrash = obj;
+	int idx;
+
+	if (thrash->sorcery) {
+		ast_sorcery_unref(thrash->sorcery);
+	}
+
+	for (idx = 0; idx < AST_VECTOR_SIZE(&thrash->threads); ++idx) {
+		struct sorcery_memory_cache_thrash_thread *thread;
+
+		thread = AST_VECTOR_GET(&thrash->threads, idx);
+		ast_free(thread);
+	}
+	AST_VECTOR_FREE(&thrash->threads);
+
+	ast_sorcery_wizard_unregister(&mock_wizard);
+}
+
+/*!
+ * \internal
+ * \brief Set up thrasing against a memory cache on a sorcery instance
+ *
+ * \param cache_configuration The sorcery memory cache configuration to use
+ * \param update_threads The number of threads which should be constantly updating sorcery
+ * \param retrieve_threads The number of threads which should be constantly retrieving from sorcery
+ * \param unique_objects The number of unique objects that can exist
+ *
+ * \retval non-NULL success
+ * \retval NULL failure
+ */
+static struct sorcery_memory_cache_thrash *sorcery_memory_cache_thrash_create(const char *cache_configuration,
+	unsigned int update_threads, unsigned int retrieve_threads, unsigned int unique_objects)
+{
+	struct sorcery_memory_cache_thrash *thrash;
+	struct sorcery_memory_cache_thrash_thread *thread;
+	unsigned int total_threads = update_threads + retrieve_threads;
+
+	thrash = ao2_alloc_options(sizeof(*thrash), sorcery_memory_cache_thrash_destroy,
+		AO2_ALLOC_OPT_LOCK_NOLOCK);
+	if (!thrash) {
+		return NULL;
+	}
+
+	thrash->update_threads = update_threads;
+	thrash->retrieve_threads = retrieve_threads;
+
+	ast_sorcery_wizard_register(&mock_wizard);
+
+	thrash->sorcery = ast_sorcery_open();
+	if (!thrash->sorcery) {
+		ao2_ref(thrash, -1);
+		return NULL;
+	}
+
+	ast_sorcery_apply_wizard_mapping(thrash->sorcery, "test", "memory_cache",
+			!strcmp(cache_configuration, "default") ? "" : cache_configuration, 1);
+	ast_sorcery_apply_wizard_mapping(thrash->sorcery, "test", "mock", NULL, 0);
+	ast_sorcery_internal_object_register(thrash->sorcery, "test", test_data_alloc, NULL, NULL);
+
+	if (AST_VECTOR_INIT(&thrash->threads, update_threads + retrieve_threads)) {
+		ao2_ref(thrash, -1);
+		return NULL;
+	}
+
+	while (AST_VECTOR_SIZE(&thrash->threads) != total_threads) {
+		thread = ast_calloc(1, sizeof(*thread));
+
+		if (!thread) {
+			ao2_ref(thrash, -1);
+			return NULL;
+		}
+
+		thread->thread = AST_PTHREADT_NULL;
+		thread->unique_objects = unique_objects;
+
+		/* This purposely holds no ref as the main thrash structure does */
+		thread->sorcery = thrash->sorcery;
+
+		AST_VECTOR_APPEND(&thrash->threads, thread);
+	}
+
+	return thrash;
+}
+
+/*!
+ * \internal
+ * \brief Thrashing cache update thread
+ *
+ * \param data The sorcery memory cache thrash thread
+ */
+static void *sorcery_memory_cache_thrash_update(void *data)
+{
+	struct sorcery_memory_cache_thrash_thread *thread = data;
+	struct timeval start;
+	unsigned int object_id;
+	char object_id_str[AST_UUID_STR_LEN];
+	void *object;
+
+	while (!thread->stop) {
+		object_id = ast_random() % thread->unique_objects;
+		snprintf(object_id_str, sizeof(object_id_str), "%u", object_id);
+
+		object = ast_sorcery_alloc(thread->sorcery, "test", object_id_str);
+		ast_assert(object != NULL);
+
+		start = ast_tvnow();
+		ast_sorcery_update(thread->sorcery, object);
+		thread->average_execution_time = (thread->average_execution_time + ast_tvdiff_ms(ast_tvnow(), start)) / 2;
+		ao2_ref(object, -1);
+	}
+
+	return NULL;
+}
+
+/*!
+ * \internal
+ * \brief Thrashing cache retrieve thread
+ *
+ * \param data The sorcery memory cache thrash thread
+ */
+static void *sorcery_memory_cache_thrash_retrieve(void *data)
+{
+	struct sorcery_memory_cache_thrash_thread *thread = data;
+	struct timeval start;
+	unsigned int object_id;
+	char object_id_str[AST_UUID_STR_LEN];
+	void *object;
+
+	while (!thread->stop) {
+		object_id = ast_random() % thread->unique_objects;
+		snprintf(object_id_str, sizeof(object_id_str), "%u", object_id);
+
+		start = ast_tvnow();
+		object = ast_sorcery_retrieve_by_id(thread->sorcery, "test", object_id_str);
+		thread->average_execution_time = (thread->average_execution_time + ast_tvdiff_ms(ast_tvnow(), start)) / 2;
+		ast_assert(object != NULL);
+
+		ao2_ref(object, -1);
+	}
+
+	return NULL;
+}
+
+/*!
+ * \internal
+ * \brief Stop thrashing against a sorcery memory cache
+ *
+ * \param thrash The sorcery memory cache thrash structure
+ */
+static void sorcery_memory_cache_thrash_stop(struct sorcery_memory_cache_thrash *thrash)
+{
+	int idx;
+
+	for (idx = 0; idx < AST_VECTOR_SIZE(&thrash->threads); ++idx) {
+		struct sorcery_memory_cache_thrash_thread *thread;
+
+		thread = AST_VECTOR_GET(&thrash->threads, idx);
+		if (thread->thread == AST_PTHREADT_NULL) {
+			continue;
+		}
+
+		thread->stop = 1;
+
+		pthread_join(thread->thread, NULL);
+
+		if (idx < thrash->update_threads) {
+			thrash->average_update_execution_time += thread->average_execution_time;
+		} else {
+			thrash->average_retrieve_execution_time += thread->average_execution_time;
+		}
+	}
+
+	if (thrash->update_threads) {
+		thrash->average_update_execution_time /= thrash->update_threads;
+	}
+	if (thrash->retrieve_threads) {
+		thrash->average_retrieve_execution_time /= thrash->retrieve_threads;
+	}
+}
+
+/*!
+ * \internal
+ * \brief Start thrashing against a sorcery memory cache
+ *
+ * \param thrash The sorcery memory cache thrash structure
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+static int sorcery_memory_cache_thrash_start(struct sorcery_memory_cache_thrash *thrash)
+{
+	int idx;
+
+	for (idx = 0; idx < AST_VECTOR_SIZE(&thrash->threads); ++idx) {
+		struct sorcery_memory_cache_thrash_thread *thread;
+
+		thread = AST_VECTOR_GET(&thrash->threads, idx);
+
+		if (ast_pthread_create(&thread->thread, NULL, idx < thrash->update_threads ?
+			sorcery_memory_cache_thrash_update : sorcery_memory_cache_thrash_retrieve, thread)) {
+			sorcery_memory_cache_thrash_stop(thrash);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+/*!
+ * \internal
+ * \brief CLI command implementation for 'sorcery memory cache thrash'
+ */
+static char *sorcery_memory_cache_cli_thrash(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	struct sorcery_memory_cache_thrash *thrash;
+	unsigned int thrash_time, unique_objects, retrieve_threads, update_threads;
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "sorcery memory cache thrash";
+		e->usage =
+		    "Usage: sorcery memory cache thrash <cache configuration> <amount of time to thrash the cache> <number of unique objects> <number of retrieve threads> <number of update threads>\n"
+		    "       Create a sorcery instance with a memory cache using the provided configuration and thrash it.\n";
+		return NULL;
+	case CLI_GENERATE:
+		return NULL;
+	}
+
+	if (a->argc != 9) {
+		return CLI_SHOWUSAGE;
+	}
+
+	if (sscanf(a->argv[5], "%30u", &thrash_time) != 1) {
+		ast_cli(a->fd, "An invalid value of '%s' has been provided for the thrashing time\n", a->argv[5]);
+		return CLI_FAILURE;
+	} else if (sscanf(a->argv[6], "%30u", &unique_objects) != 1) {
+		ast_cli(a->fd, "An invalid value of '%s' has been provided for number of unique objects\n", a->argv[6]);
+		return CLI_FAILURE;
+	} else if (sscanf(a->argv[7], "%30u", &retrieve_threads) != 1) {
+		ast_cli(a->fd, "An invalid value of '%s' has been provided for the number of retrieve threads\n", a->argv[7]);
+		return CLI_FAILURE;
+	} else if (sscanf(a->argv[8], "%30u", &update_threads) != 1) {
+		ast_cli(a->fd, "An invalid value of '%s' has been provided for the number of update threads\n", a->argv[8]);
+		return CLI_FAILURE;
+	}
+
+	thrash = sorcery_memory_cache_thrash_create(a->argv[4], update_threads, retrieve_threads, unique_objects);
+	if (!thrash) {
+		ast_cli(a->fd, "Could not create a sorcery memory cache thrash test using the provided arguments\n");
+		return CLI_FAILURE;
+	}
+
+	ast_cli(a->fd, "Starting cache thrash test.\n");
+	ast_cli(a->fd, "Memory cache configuration: %s\n", a->argv[4]);
+	ast_cli(a->fd, "Amount of time to perform test: %u seconds\n", thrash_time);
+	ast_cli(a->fd, "Number of unique objects: %u\n", unique_objects);
+	ast_cli(a->fd, "Number of retrieve threads: %u\n", retrieve_threads);
+	ast_cli(a->fd, "Number of update threads: %u\n", update_threads);
+
+	sorcery_memory_cache_thrash_start(thrash);
+	while ((thrash_time = sleep(thrash_time)));
+	sorcery_memory_cache_thrash_stop(thrash);
+
+	ast_cli(a->fd, "Stopped cache thrash test\n");
+
+	ast_cli(a->fd, "Average retrieve execution time (in milliseconds): %u\n", thrash->average_retrieve_execution_time);
+	ast_cli(a->fd, "Average update execution time (in milliseconds): %u\n", thrash->average_update_execution_time);
+
+	ao2_ref(thrash, -1);
+
+	return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_memory_cache_thrash[] = {
+	AST_CLI_DEFINE(sorcery_memory_cache_cli_thrash, "Thrash a sorcery memory cache"),
+};
+
+/*!
+ * \internal
+ * \brief Perform a thrash test against a cache
+ *
+ * \param test The unit test being run
+ * \param cache_configuration The underlying cache configuration
+ * \param thrash_time How long (in seconds) to thrash the cache for
+ * \param unique_objects The number of unique objects
+ * \param retrieve_threads The number of threads constantly doing a retrieve
+ * \param update_threads The number of threads constantly doing an update
+ *
+ * \retval AST_TEST_PASS success
+ * \retval AST_TEST_FAIL failure
+ */
+static enum ast_test_result_state nominal_thrash(struct ast_test *test, const char *cache_configuration,
+	unsigned int thrash_time, unsigned int unique_objects, unsigned int retrieve_threads,
+	unsigned int update_threads)
+{
+	struct sorcery_memory_cache_thrash *thrash;
+
+	thrash = sorcery_memory_cache_thrash_create(cache_configuration, update_threads, retrieve_threads, unique_objects);
+	if (!thrash) {
+		return AST_TEST_FAIL;
+	}
+
+	sorcery_memory_cache_thrash_start(thrash);
+	while ((thrash_time = sleep(thrash_time)));
+	sorcery_memory_cache_thrash_stop(thrash);
+
+	ao2_ref(thrash, -1);
+
+	return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(low_unique_object_count_immediately_stale)
+{
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "low_unique_object_count_immediately_stale";
+		info->category = "/res/res_sorcery_memory_cache/thrash/";
+		info->summary = "Thrash a cache with low number of unique objects that are immediately stale";
+		info->description = "This test creates a cache with objects that are stale\n"
+			"after 1 second. It also creates 25 threads which are constantly attempting\n"
+			"to retrieve the objects. This test confirms that the background refreshes\n"
+			"being done as a result of going stale do not conflict or cause problems with\n"
+			"the large number of retrieve threads.";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	return nominal_thrash(test, "object_lifetime_stale=1", TEST_THRASH_TIME, 10, TEST_THRASH_RETRIEVERS, 0);
+}
+
+AST_TEST_DEFINE(low_unique_object_count_immediately_expire)
+{
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "low_unique_object_count_immediately_expire";
+		info->category = "/res/res_sorcery_memory_cache/thrash/";
+		info->summary = "Thrash a cache with low number of unique objects that are immediately expired";
+		info->description = "This test creates a cache with objects that are expired\n"
+			"after 1 second. It also creates 25 threads which are constantly attempting\n"
+			"to retrieve the objects. This test confirms that the expiration process does\n"
+			"not cause a problem as the retrieve threads execute.";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	return nominal_thrash(test, "object_lifetime_maximum=1", TEST_THRASH_TIME, 10, TEST_THRASH_RETRIEVERS, 0);
+}
+
+AST_TEST_DEFINE(low_unique_object_count_high_concurrent_updates)
+{
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "low_unique_object_count_high_concurrent_updates";
+		info->category = "/res/res_sorcery_memory_cache/thrash/";
+		info->summary = "Thrash a cache with low number of unique objects that are updated frequently";
+		info->description = "This test creates a cache with objects that are being constantly\n"
+			"updated and retrieved at the same time. This will create contention between all\n"
+			"of the threads as the write lock is held for the updates. This test confirms that\n"
+			"no problems occur in this situation.";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	return nominal_thrash(test, "default", TEST_THRASH_TIME, 10, TEST_THRASH_RETRIEVERS, TEST_THRASH_UPDATERS);
+}
+
+AST_TEST_DEFINE(unique_objects_exceeding_maximum)
+{
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "unique_objects_exceeding_maximum";
+		info->category = "/res/res_sorcery_memory_cache/thrash/";
+		info->summary = "Thrash a cache with a fixed maximum object count";
+		info->description = "This test creates a cache with a maximum number of objects\n"
+			"allowed in it. The maximum number of unique objects, however, far exceeds the\n"
+			"the maximum number allowed in the cache. This test confirms that the cache does\n"
+			"not exceed the maximum and that the removal of older objects does not cause\n"
+			"a problem.";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	return nominal_thrash(test, "maximum_objects=10", TEST_THRASH_TIME, 100, TEST_THRASH_RETRIEVERS, 0);
+}
+
+AST_TEST_DEFINE(unique_objects_exceeding_maximum_with_expire_and_stale)
+{
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "unique_objects_exceeding_maximum_with_expire_and_stale";
+		info->category = "/res/res_sorcery_memory_cache/thrash/";
+		info->summary = "Thrash a cache with a fixed maximum object count with objects that expire and go stale";
+		info->description = "This test creates a cache with a maximum number of objects\n"
+			"allowed in it with objects that also go stale after a period of time and expire.\n"
+			"A number of threads are created that constantly retrieve from the cache, causing\n"
+			"both stale refresh and expiration to occur. This test confirms that the combination\n"
+			"of these do not present a problem.";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	return nominal_thrash(test, "maximum_objects=10,object_lifetime_maximum=2,object_lifetime_stale=1",
+		TEST_THRASH_TIME * 2, 100, TEST_THRASH_RETRIEVERS, 0);
+}
+
+AST_TEST_DEFINE(conflicting_expire_and_stale)
+{
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "conflicting_expire_and_stale";
+		info->category = "/res/res_sorcery_memory_cache/thrash/";
+		info->summary = "Thrash a cache with a large number of objects that expire and go stale";
+		info->description = "This test creates a cache with a large number of objects that expire\n"
+			"and go stale. As there is such a large number this ensures that both operations occur.\n"
+			"This test confirms that stale refreshing and expiration do not conflict.";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	return nominal_thrash(test, "object_lifetime_maximum=2,object_lifetime_stale=1", TEST_THRASH_TIME * 2, 5000,
+		TEST_THRASH_RETRIEVERS, 0);
+}
+
+AST_TEST_DEFINE(high_object_count_without_expiration)
+{
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "high_object_count_without_expiration";
+		info->category = "/res/res_sorcery_memory_cache/thrash/";
+		info->summary = "Thrash a cache with a large number of objects";
+		info->description = "This test creates a cache with a large number of objects that persist.\n"
+			"A large number of threads are created which constantly retrieve from the cache.\n"
+			"This test confirms that the large number of retrieves do not cause a problem.";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	return nominal_thrash(test, "default", TEST_THRASH_TIME, 5000, TEST_THRASH_RETRIEVERS, 0);
+}
+
+static int unload_module(void)
+{
+	ast_cli_unregister_multiple(cli_memory_cache_thrash, ARRAY_LEN(cli_memory_cache_thrash));
+	AST_TEST_UNREGISTER(low_unique_object_count_immediately_stale);
+	AST_TEST_UNREGISTER(low_unique_object_count_immediately_expire);
+	AST_TEST_UNREGISTER(low_unique_object_count_high_concurrent_updates);
+	AST_TEST_UNREGISTER(unique_objects_exceeding_maximum);
+	AST_TEST_UNREGISTER(unique_objects_exceeding_maximum_with_expire_and_stale);
+	AST_TEST_UNREGISTER(conflicting_expire_and_stale);
+	AST_TEST_UNREGISTER(high_object_count_without_expiration);
+
+	return 0;
+}
+
+static int load_module(void)
+{
+	ast_cli_register_multiple(cli_memory_cache_thrash, ARRAY_LEN(cli_memory_cache_thrash));
+	AST_TEST_REGISTER(low_unique_object_count_immediately_stale);
+	AST_TEST_REGISTER(low_unique_object_count_immediately_expire);
+	AST_TEST_REGISTER(low_unique_object_count_high_concurrent_updates);
+	AST_TEST_REGISTER(unique_objects_exceeding_maximum);
+	AST_TEST_REGISTER(unique_objects_exceeding_maximum_with_expire_and_stale);
+	AST_TEST_REGISTER(conflicting_expire_and_stale);
+	AST_TEST_REGISTER(high_object_count_without_expiration);
+
+	return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Sorcery Cache Thrasing test module");
diff --git a/tests/test_sorcery_realtime.c b/tests/test_sorcery_realtime.c
index ab9c188..eaee9ff 100644
--- a/tests/test_sorcery_realtime.c
+++ b/tests/test_sorcery_realtime.c
@@ -67,17 +67,13 @@ static int realtime_is_object_matching(const char *object_id, const struct ast_v
 
 		/* If we are doing a pattern matching we need to remove the LIKE from the name */
 		if ((like = strstr(name, " LIKE"))) {
-			char *pattern, *field_value = ast_strdupa(field->value);
+			char *field_value = ast_strdupa(field->value);
 
 			*like = '\0';
 
 			value = ast_strdupa(ast_variable_retrieve(realtime_objects, object_id, name));
 
-			if (!(pattern = strchr(field_value, '%'))) {
-				return 0;
-			}
-
-			*pattern = '\0';
+			field_value = ast_strip_quoted(field_value, "%", "%");
 
 			if (strncmp(value, field_value, strlen(field_value))) {
 				return 0;
@@ -567,7 +563,7 @@ AST_TEST_DEFINE(object_retrieve_regex)
 		return AST_TEST_FAIL;
 	}
 
-	if (!(objects = ast_sorcery_retrieve_by_regex(sorcery, "test", "^blah-"))) {
+	if (!(objects = ast_sorcery_retrieve_by_regex(sorcery, "test", "blah-"))) {
 		ast_test_status_update(test, "Failed to retrieve a container of objects\n");
 		return AST_TEST_FAIL;
 	} else if (ao2_container_count(objects) != 2) {
@@ -821,7 +817,7 @@ AST_TEST_DEFINE(object_filter)
 			"realtime backend that is unknown to sorcery. When sorcery attempts to retrieve\n"
 			"the object from the realtime backend, the data unknown to sorcery should be\n"
 			"filtered out of the returned objectset, and the object should be successfully\n"
-			"allocated by sorcery\n";
+			"allocated by sorcery";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
diff --git a/tests/test_stasis.c b/tests/test_stasis.c
index b4ea07e..e01be94 100644
--- a/tests/test_stasis.c
+++ b/tests/test_stasis.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428815 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/astobj2.h"
 #include "asterisk/module.h"
@@ -788,7 +788,7 @@ AST_TEST_DEFINE(subscription_interleaving)
 			"and publishes messages alternately between the children.\n"
 			"It verifies that the messages are received in the expected\n"
 			"order, for different subscription types: one with a dedicated\n"
-			"thread, the other on the Stasis threadpool.\n";
+			"thread, the other on the Stasis threadpool.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -1617,7 +1617,7 @@ AST_TEST_DEFINE(router_pool)
 		info->summary = "Test message routing via threadpool";
 		info->description = "Test simple message routing when\n"
 			"the subscriptions dictate usage of the Stasis\n"
-			"threadpool.\n";
+			"threadpool.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
diff --git a/tests/test_stasis_channels.c b/tests/test_stasis_channels.c
index 3b5350a..2a36c4f 100644
--- a/tests/test_stasis_channels.c
+++ b/tests/test_stasis_channels.c
@@ -31,7 +31,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420124 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/astobj2.h"
 #include "asterisk/module.h"
@@ -278,6 +278,7 @@ AST_TEST_DEFINE(channel_snapshot_json)
 				 "  s: { s: s, s: s, s: i },"
 				 "  s: { s: s, s: s },"
 				 "  s: { s: s, s: s },"
+				 "  s: s"
 				 "  s: o"
 				 "}",
 				 "name", "TEST/name",
@@ -294,6 +295,7 @@ AST_TEST_DEFINE(channel_snapshot_json)
 				 "connected",
 				 "name", "",
 				 "number", "",
+				 "language", "en",
 				 "creationtime",
 				 ast_json_timeval(
 					 ast_channel_creationtime(chan), NULL));
diff --git a/tests/test_stasis_endpoints.c b/tests/test_stasis_endpoints.c
index 51f5274..7ac5291 100644
--- a/tests/test_stasis_endpoints.c
+++ b/tests/test_stasis_endpoints.c
@@ -32,7 +32,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419319 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/astobj2.h"
 #include "asterisk/channel.h"
diff --git a/tests/test_stringfields.c b/tests/test_stringfields.c
index 7123480..2eeb83d 100644
--- a/tests/test_stringfields.c
+++ b/tests/test_stringfields.c
@@ -212,7 +212,7 @@ AST_TEST_DEFINE(string_field_test)
 	}
 
 	if (AST_STRING_FIELD_ALLOCATION(test_struct.string2) != strlen("hippopotamus face") + 1) {
-		ast_test_status_update(test, "The allocation amount is incorrect for string2. We expect %lu but it has %hu\n",
+		ast_test_status_update(test, "The allocation amount is incorrect for string2. We expect %lu but it has %d\n",
 				(unsigned long) strlen("hippopotamus face"), AST_STRING_FIELD_ALLOCATION(test_struct.string2) + 1);
 		goto error;
 	} else {
diff --git a/tests/test_strings.c b/tests/test_strings.c
index 394057e..5e3446d 100644
--- a/tests/test_strings.c
+++ b/tests/test_strings.c
@@ -34,7 +34,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 427356 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/test.h"
 #include "asterisk/utils.h"
@@ -390,9 +390,12 @@ AST_TEST_DEFINE(strsep_test)
 static int test_semi(char *string1, char *string2, int test_len)
 {
 	char *test2 = NULL;
-	if (test_len >= 0) {
+
+	if (test_len > 0) {
 		test2 = ast_alloca(test_len);
 		*test2 = '\0';
+	} else if (test_len == 0) {
+		test2 = "";
 	}
 	ast_escape_semicolons(string1, test2, test_len);
 	if (test2 != NULL && strcmp(string2, test2) == 0) {
@@ -452,6 +455,74 @@ AST_TEST_DEFINE(escape_semicolons_test)
 	return AST_TEST_PASS;
 }
 
+AST_TEST_DEFINE(escape_test)
+{
+	char buf[128];
+
+#define TEST_ESCAPE(s, to_escape, expected) \
+	!strcmp(ast_escape(buf, s, ARRAY_LEN(buf), to_escape), expected)
+
+#define TEST_ESCAPE_C(s, expected) \
+	!strcmp(ast_escape_c(buf, s, ARRAY_LEN(buf)), expected)
+
+#define TEST_ESCAPE_ALLOC(s, to_escape, expected)		\
+	({													\
+		int res = 0;									\
+		char *a_buf = ast_escape_alloc(s, to_escape);	\
+		if (a_buf) {									\
+			res = !strcmp(a_buf, expected);				\
+			ast_free(a_buf);							\
+		}												\
+		res;											\
+	})
+
+#define TEST_ESCAPE_C_ALLOC(s, expected)				\
+	({													\
+		int res = 0;									\
+		char *a_buf = ast_escape_c_alloc(s);			\
+		if (a_buf) {									\
+			res = !strcmp(a_buf, expected);				\
+			ast_free(a_buf);							\
+		}												\
+		res;											\
+	})
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "escape";
+		info->category = "/main/strings/";
+		info->summary = "Test ast_escape";
+		info->description = "Test escaping values in a string";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	ast_test_validate(test, TEST_ESCAPE("null escape", NULL, "null escape"));
+	ast_test_validate(test, TEST_ESCAPE("empty escape", "", "empty escape"));
+	ast_test_validate(test, TEST_ESCAPE("", "Z", ""));
+	ast_test_validate(test, TEST_ESCAPE("no matching escape", "Z", "no matching escape"));
+	ast_test_validate(test, TEST_ESCAPE("escape Z", "Z", "escape \\Z"));
+	ast_test_validate(test, TEST_ESCAPE("Z", "Z", "\\Z"));
+	ast_test_validate(test, TEST_ESCAPE(";;", ";", "\\;\\;"));
+	ast_test_validate(test, TEST_ESCAPE("escape \n", "\n", "escape \\n"));
+	ast_test_validate(test, TEST_ESCAPE("escape \n again \n", "\n", "escape \\n again \\n"));
+
+	ast_test_validate(test, TEST_ESCAPE_C("", ""));
+	ast_test_validate(test, TEST_ESCAPE_C("escape \a\b\f\n\r\t\v\\\'\"\?",
+		"escape \\a\\b\\f\\n\\r\\t\\v\\\\\\\'\\\"\\?"));
+
+	ast_test_validate(test, TEST_ESCAPE_ALLOC("", "Z", ""));
+	ast_test_validate(test, TEST_ESCAPE_ALLOC("Z", "Z", "\\Z"));
+	ast_test_validate(test, TEST_ESCAPE_ALLOC("a", "Z", "a"));
+
+	ast_test_validate(test, TEST_ESCAPE_C_ALLOC("", ""));
+	ast_test_validate(test, TEST_ESCAPE_C_ALLOC("\n", "\\n"));
+	ast_test_validate(test, TEST_ESCAPE_C_ALLOC("a", "a"));
+
+	return AST_TEST_PASS;
+}
+
 static int unload_module(void)
 {
 	AST_TEST_UNREGISTER(str_test);
@@ -459,6 +530,7 @@ static int unload_module(void)
 	AST_TEST_UNREGISTER(ends_with_test);
 	AST_TEST_UNREGISTER(strsep_test);
 	AST_TEST_UNREGISTER(escape_semicolons_test);
+	AST_TEST_UNREGISTER(escape_test);
 	return 0;
 }
 
@@ -469,6 +541,7 @@ static int load_module(void)
 	AST_TEST_REGISTER(ends_with_test);
 	AST_TEST_REGISTER(strsep_test);
 	AST_TEST_REGISTER(escape_semicolons_test);
+	AST_TEST_REGISTER(escape_test);
 	return AST_MODULE_LOAD_SUCCESS;
 }
 
diff --git a/tests/test_substitution.c b/tests/test_substitution.c
index e3b2247..46e8ce9 100644
--- a/tests/test_substitution.c
+++ b/tests/test_substitution.c
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 410158 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/file.h"
 #include "asterisk/channel.h"
diff --git a/tests/test_threadpool.c b/tests/test_threadpool.c
index 79b369d..42181a2 100644
--- a/tests/test_threadpool.c
+++ b/tests/test_threadpool.c
@@ -571,6 +571,87 @@ end:
 	return res;
 }
 
+AST_TEST_DEFINE(threadpool_thread_timeout_thrash)
+{
+	struct ast_threadpool *pool = NULL;
+	struct ast_threadpool_listener *listener = NULL;
+	enum ast_test_result_state res = AST_TEST_FAIL;
+	struct test_listener_data *tld = NULL;
+	struct ast_threadpool_options options = {
+		.version = AST_THREADPOOL_OPTIONS_VERSION,
+		.idle_timeout = 1,
+		.auto_increment = 1,
+		.initial_size = 0,
+		.max_size = 1,
+	};
+	int iteration;
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "thread_timeout_thrash";
+		info->category = "/main/threadpool/";
+		info->summary = "Thrash threadpool thread timeout";
+		info->description =
+			"Repeatedly queue a task when a threadpool thread should timeout.";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	tld = test_alloc();
+	if (!tld) {
+		return AST_TEST_FAIL;
+	}
+
+	listener = ast_threadpool_listener_alloc(&test_callbacks, tld);
+	if (!listener) {
+		goto end;
+	}
+
+	pool = ast_threadpool_create(info->name, listener, &options);
+	if (!pool) {
+		goto end;
+	}
+
+	ast_threadpool_set_size(pool, 1);
+
+	for (iteration = 0; iteration < 30; ++iteration) {
+		struct simple_task_data *std = NULL;
+		struct timeval start = ast_tvnow();
+		struct timespec end = {
+			.tv_sec = start.tv_sec + options.idle_timeout,
+			.tv_nsec = start.tv_usec * 1000
+		};
+
+		std = simple_task_data_alloc();
+		if (!std) {
+			goto end;
+		}
+
+		/* Wait until the threadpool thread should timeout due to being idle */
+		ast_mutex_lock(&tld->lock);
+		while (ast_cond_timedwait(&tld->cond, &tld->lock, &end) != ETIMEDOUT) {
+			/* This purposely left empty as we want to loop waiting for a time out */
+		}
+		ast_mutex_unlock(&tld->lock);
+
+		ast_threadpool_push(pool, simple_task, std);
+	}
+
+	res = wait_until_thread_state(test, tld, 0, 0);
+	if (res == AST_TEST_FAIL) {
+		goto end;
+	}
+
+	res = listener_check(test, listener, 1, 1, 30, 0, 0, 1);
+
+end:
+	ast_threadpool_shutdown(pool);
+	ao2_cleanup(listener);
+	ast_free(tld);
+	return res;
+}
+
 AST_TEST_DEFINE(threadpool_one_task_one_thread)
 {
 	struct ast_threadpool *pool = NULL;
@@ -858,7 +939,7 @@ AST_TEST_DEFINE(threadpool_auto_increment)
 		info->description =
 			"Create an empty threadpool and push a task to it. Once the task is\n"
 			"pushed, the threadpool should add three threads and be able to\n"
-			"handle the task. The threads should then go idle\n";
+			"handle the task. The threads should then go idle";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -972,7 +1053,7 @@ AST_TEST_DEFINE(threadpool_max_size)
 		info->description =
 			"Create an empty threadpool and push a task to it. Once the task is\n"
 			"pushed, the threadpool should attempt to grow by three threads, but the\n"
-			"pool's restrictions should only allow two threads to be added.\n";
+			"pool's restrictions should only allow two threads to be added.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -1043,7 +1124,7 @@ AST_TEST_DEFINE(threadpool_reactivation)
 		info->description =
 			"Push a task into a threadpool. Make sure the task executes and the\n"
 			"thread goes idle. Then push a second task and ensure that the thread\n"
-			"awakens and executes the second task.\n";
+			"awakens and executes the second task.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -1250,7 +1331,7 @@ AST_TEST_DEFINE(threadpool_task_distribution)
 		info->summary = "Test that tasks are evenly distributed to threads";
 		info->description =
 			"Push two tasks into a threadpool. Ensure that each is handled by\n"
-			"a separate thread\n";
+			"a separate thread";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -1346,7 +1427,7 @@ AST_TEST_DEFINE(threadpool_more_destruction)
 			"Push two tasks into a threadpool. Set the threadpool size to 4\n"
 			"Ensure that there are 2 active and 2 idle threads. Then shrink the\n"
 			"threadpool down to 1 thread. Ensure that the thread leftover is active\n"
-			"and ensure that both tasks complete.\n";
+			"and ensure that both tasks complete.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -1456,7 +1537,7 @@ AST_TEST_DEFINE(threadpool_serializer)
 		info->category = "/main/threadpool/";
 		info->summary = "Test that serializers";
 		info->description =
-			"Ensures that tasks enqueued to a serialize execute in sequence.\n";
+			"Ensures that tasks enqueued to a serialize execute in sequence.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -1570,7 +1651,7 @@ AST_TEST_DEFINE(threadpool_serializer_dupe)
 		info->summary = "Test that serializers are uniquely named";
 		info->description =
 			"Creating two serializers with the same name should\n"
-			"result in error.\n";
+			"result in error.";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -1610,6 +1691,7 @@ static int unload_module(void)
 	ast_test_unregister(threadpool_thread_creation);
 	ast_test_unregister(threadpool_thread_destruction);
 	ast_test_unregister(threadpool_thread_timeout);
+	ast_test_unregister(threadpool_thread_timeout_thrash);
 	ast_test_unregister(threadpool_one_task_one_thread);
 	ast_test_unregister(threadpool_one_thread_one_task);
 	ast_test_unregister(threadpool_one_thread_multiple_tasks);
@@ -1630,6 +1712,7 @@ static int load_module(void)
 	ast_test_register(threadpool_thread_creation);
 	ast_test_register(threadpool_thread_destruction);
 	ast_test_register(threadpool_thread_timeout);
+	ast_test_register(threadpool_thread_timeout_thrash);
 	ast_test_register(threadpool_one_task_one_thread);
 	ast_test_register(threadpool_one_thread_one_task);
 	ast_test_register(threadpool_one_thread_multiple_tasks);
diff --git a/tests/test_time.c b/tests/test_time.c
index 9ea72f6..ef6e954 100644
--- a/tests/test_time.c
+++ b/tests/test_time.c
@@ -32,7 +32,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 389251 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/utils.h"
 #include "asterisk/app.h"
diff --git a/tests/test_utils.c b/tests/test_utils.c
index 395a531..e4f6e0a 100644
--- a/tests/test_utils.c
+++ b/tests/test_utils.c
@@ -32,7 +32,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 422154 $");
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
 
 #include "asterisk/utils.h"
 #include "asterisk/test.h"
diff --git a/tests/test_vector.c b/tests/test_vector.c
new file mode 100644
index 0000000..8ca4efa
--- /dev/null
+++ b/tests/test_vector.c
@@ -0,0 +1,517 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2015, Fairview 5 Engineering, LLC
+ *
+ * George Joseph <george.joseph at fairview5.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 Vector tests
+ *
+ * \author George Joseph <george.joseph at fairview5.com>
+ *
+ * This module will run some vector tests.
+ *
+ * \ingroup tests
+ */
+
+/*** MODULEINFO
+	<depend>TEST_FRAMEWORK</depend>
+	<support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+#include "asterisk/test.h"
+#include "asterisk/utils.h"
+#include "asterisk/strings.h"
+#include "asterisk/module.h"
+#include "asterisk/vector.h"
+
+static int cleanup_count;
+
+static void cleanup(char *element)
+{
+	cleanup_count++;
+}
+
+#define STRING_CMP(a, b) ({ \
+	((a) == NULL || (b) == NULL) ? -1 : (strcmp((a), (b)) == 0); \
+})
+
+AST_TEST_DEFINE(basic_ops)
+{
+	AST_VECTOR(test_struct, char *) sv1;
+	int rc = AST_TEST_PASS;
+
+	char *AAA = "AAA";
+	char *BBB = "BBB";
+	char *CCC = "CCC";
+	char *YYY = "YYY";
+	char *ZZZ = "ZZZ";
+	char CCC2[4];
+
+	strcpy(CCC2, "CCC");
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "basic";
+		info->category = "/main/vector/";
+		info->summary = "Test vector basic ops";
+		info->description = "Test vector basic ops";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	ast_test_validate(test, AST_VECTOR_INIT(&sv1, 3) == 0);
+	ast_test_validate_cleanup(test, sv1.max == 3, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_SIZE(&sv1) == 0, rc, cleanup);
+	/* there should be no vector growth for the 3 appends */
+	ast_test_validate_cleanup(test, AST_VECTOR_APPEND(&sv1, AAA) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, sv1.max == 3, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_APPEND(&sv1, BBB) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, sv1.max == 3, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_APPEND(&sv1, CCC) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_SIZE(&sv1) == 3, rc, cleanup);
+	ast_test_validate_cleanup(test, sv1.max >= 3, 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);
+	ast_test_validate_cleanup(test, AST_VECTOR_GET(&sv1, 2) == CCC, rc, cleanup);
+	ast_test_validate_cleanup(test, sv1.max == sv1.current, rc, cleanup);
+
+	ast_test_validate_cleanup(test, AST_VECTOR_INSERT_AT(&sv1, 1, ZZZ) == 0, rc, cleanup);
+	/* The vector should have grown */
+	ast_test_validate_cleanup(test, sv1.max == 8, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_SIZE(&sv1) == 4, 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) == ZZZ, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_GET(&sv1, 2) == BBB, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_GET(&sv1, 3) == CCC, rc, cleanup);
+
+	/* Test inserting > current but < max */
+	ast_test_validate_cleanup(test, AST_VECTOR_INSERT_AT(&sv1, 6, YYY) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, sv1.current == 7, rc, cleanup);
+	/* The vector should not have grown */
+	ast_test_validate_cleanup(test, sv1.max == 8, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_GET(&sv1, 6) == YYY, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_GET(&sv1, 4) == NULL, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_GET(&sv1, 5) == NULL, rc, cleanup);
+	ast_test_validate_cleanup(test, *(char **)AST_VECTOR_GET_CMP(&sv1, "AAA", STRING_CMP) == AAA, rc, cleanup);
+	ast_test_validate_cleanup(test, *(char **)AST_VECTOR_GET_CMP(&sv1, "ZZZ", STRING_CMP) == ZZZ, rc, cleanup);
+
+	/* Test inserting > max */
+	ast_test_validate_cleanup(test, AST_VECTOR_INSERT_AT(&sv1, 12, AAA) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, sv1.current == 13, rc, cleanup);
+	/* The vector should have grown */
+	ast_test_validate_cleanup(test, sv1.max == 26, rc, cleanup);
+
+	/* RESET */
+	AST_VECTOR_FREE(&sv1);
+	ast_test_validate(test, sv1.elems == NULL);
+	ast_test_validate(test, sv1.current == 0);
+	ast_test_validate(test, sv1.max == 0);
+
+	/* Test with initial size = 0 */
+	ast_test_validate(test, AST_VECTOR_INIT(&sv1, 0) == 0);
+	ast_test_validate_cleanup(test, sv1.max == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_SIZE(&sv1) == 0, rc, cleanup);
+
+	ast_test_validate_cleanup(test, AST_VECTOR_APPEND(&sv1, AAA) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_APPEND(&sv1, BBB) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_APPEND(&sv1, CCC) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, sv1.max >= 3, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_SIZE(&sv1) == 3, 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);
+	ast_test_validate_cleanup(test, AST_VECTOR_GET(&sv1, 2) == CCC, rc, cleanup);
+
+	/* Overwrite index 1 */
+	ast_test_validate_cleanup(test, AST_VECTOR_REPLACE(&sv1, 1, ZZZ) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, sv1.current == 3, 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) == ZZZ, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_GET(&sv1, 2) == CCC, rc, cleanup);
+
+	/* Replace beyond current */
+	ast_test_validate_cleanup(test, AST_VECTOR_REPLACE(&sv1, 10, YYY) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, sv1.current == 11, 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) == ZZZ, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_GET(&sv1, 2) == CCC, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_GET(&sv1, 5) == NULL, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_GET(&sv1, 10) == YYY, rc, cleanup);
+
+	/* Replace beyond max */
+	ast_test_validate_cleanup(test, AST_VECTOR_REPLACE(&sv1, 100, YYY) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, sv1.current == 101, rc, cleanup);
+	ast_test_validate_cleanup(test, sv1.max >= 101, rc, cleanup);
+
+	/* Remove index 0 and bring the last entry (10/YYY) into it's empty slot */
+	ast_test_validate_cleanup(test, AST_VECTOR_REMOVE_UNORDERED(&sv1, 0) == AAA, rc, cleanup);
+	ast_test_validate_cleanup(test, sv1.current == 100, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_GET(&sv1, 0) == YYY, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_GET(&sv1, 1) == ZZZ, rc, cleanup);
+
+	/* Replace 0 and 2 leaving 1 alone */
+	ast_test_validate_cleanup(test, AST_VECTOR_REPLACE(&sv1, 0, AAA) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_REPLACE(&sv1, 2, CCC) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, sv1.current == 100, 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) == ZZZ, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_GET(&sv1, 2) == CCC, rc, cleanup);
+
+	/* Remove 1 and compact preserving order */
+	ast_test_validate_cleanup(test, AST_VECTOR_REMOVE_ORDERED(&sv1, 1) == ZZZ, rc, cleanup);
+	ast_test_validate_cleanup(test, sv1.current == 99, 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) == CCC, rc, cleanup);
+
+	ast_test_validate_cleanup(test, AST_VECTOR_INSERT_AT(&sv1, 0, ZZZ) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, sv1.current == 100, rc, cleanup);
+
+	/* This should fail because comparison is by pointer */
+	ast_test_validate_cleanup(test, AST_VECTOR_REMOVE_ELEM_ORDERED(&sv1, "ZZZ", cleanup) != 0, rc, cleanup);
+
+	/* This should work because we passing in the specific object to be removed */
+	cleanup_count = 0;
+	ast_test_validate_cleanup(test, AST_VECTOR_REMOVE_ELEM_ORDERED(&sv1, ZZZ, cleanup) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, sv1.current == 99, 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) == CCC, rc, cleanup);
+	ast_test_validate_cleanup(test, cleanup_count == 1, rc, cleanup);
+
+	/* If we want a comparison by value, we need to pass in a comparison
+	 * function.
+	 */
+	cleanup_count = 0;
+	ast_test_validate_cleanup(test, AST_VECTOR_REMOVE_CMP_ORDERED(&sv1, "AAA", STRING_CMP, cleanup) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_SIZE(&sv1) == 98, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_GET(&sv1, 0) == CCC, rc, cleanup);
+	ast_test_validate_cleanup(test, cleanup_count == 1, rc, cleanup);
+
+	/* Test INSERT_SORTED */
+	AST_VECTOR_FREE(&sv1);
+	ast_test_validate(test, AST_VECTOR_INIT(&sv1, 0) == 0);
+
+	ast_test_validate_cleanup(test, AST_VECTOR_ADD_SORTED(&sv1, BBB, strcmp) == 0, rc, cleanup);
+	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_GET(&sv1, 0) == AAA, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_GET(&sv1, 1) == BBB, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_GET(&sv1, 2) == CCC, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_GET(&sv1, 3) == CCC2, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_GET(&sv1, 4) == ZZZ, rc, cleanup);
+
+	cleanup_count = 0;
+	AST_VECTOR_RESET(&sv1, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_SIZE(&sv1) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, sv1.max >= 5, rc, cleanup);
+	ast_test_validate_cleanup(test, sv1.elems != NULL, rc, cleanup);
+	ast_test_validate_cleanup(test, cleanup_count == 5, rc, cleanup);
+
+cleanup:
+	AST_VECTOR_FREE(&sv1);
+	return rc;
+}
+
+static void cleanup_int(int element)
+{
+	cleanup_count++;
+}
+
+AST_TEST_DEFINE(basic_ops_integer)
+{
+	AST_VECTOR(test_struct, int) sv1;
+	int rc = AST_TEST_PASS;
+
+	int AAA = 1;
+	int BBB = 3;
+	int CCC = 5;
+	int ZZZ = 26;
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "basic_integer";
+		info->category = "/main/vector/";
+		info->summary = "Test integer vector basic ops";
+		info->description = "Test integer vector basic ops";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	ast_test_validate(test, AST_VECTOR_INIT(&sv1, 3) == 0);
+	ast_test_validate_cleanup(test, sv1.max == 3, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_SIZE(&sv1) == 0, rc, cleanup);
+
+	ast_test_validate_cleanup(test, AST_VECTOR_APPEND(&sv1, AAA) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, sv1.max == 3, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_APPEND(&sv1, BBB) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, sv1.max == 3, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_APPEND(&sv1, CCC) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_SIZE(&sv1) == 3, rc, cleanup);
+	ast_test_validate_cleanup(test, sv1.max == 3, 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);
+	ast_test_validate_cleanup(test, AST_VECTOR_GET(&sv1, 2) == CCC, rc, cleanup);
+
+	ast_test_validate_cleanup(test, AST_VECTOR_INSERT_AT(&sv1, 1, ZZZ) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, sv1.max >= 4, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_SIZE(&sv1) == 4, 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) == ZZZ, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_GET(&sv1, 2) == BBB, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_GET(&sv1, 3) == CCC, rc, cleanup);
+
+	ast_test_validate_cleanup(test, *(int *)AST_VECTOR_GET_CMP(&sv1, AAA,  AST_VECTOR_ELEM_DEFAULT_CMP) == AAA, rc, cleanup);
+	ast_test_validate_cleanup(test, *(int *)AST_VECTOR_GET_CMP(&sv1, ZZZ, AST_VECTOR_ELEM_DEFAULT_CMP) == ZZZ, rc, cleanup);
+
+	AST_VECTOR_FREE(&sv1);
+	ast_test_validate(test, sv1.elems == NULL);
+	ast_test_validate(test, sv1.current == 0);
+	ast_test_validate(test, sv1.max == 0);
+
+	ast_test_validate(test, AST_VECTOR_INIT(&sv1, 0) == 0);
+	ast_test_validate_cleanup(test, sv1.max == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_SIZE(&sv1) == 0, rc, cleanup);
+
+	ast_test_validate_cleanup(test, AST_VECTOR_APPEND(&sv1, AAA) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_APPEND(&sv1, BBB) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_APPEND(&sv1, CCC) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, sv1.max >= 3, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_SIZE(&sv1) == 3, 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);
+	ast_test_validate_cleanup(test, AST_VECTOR_GET(&sv1, 2) == CCC, rc, cleanup);
+
+	/* Overwrite index 1 */
+	ast_test_validate_cleanup(test, AST_VECTOR_REPLACE(&sv1, 1, ZZZ) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_SIZE(&sv1) == 3, 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) == ZZZ, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_GET(&sv1, 2) == CCC, rc, cleanup);
+
+	/* Remove index 0 and bring the last entry into it's empty slot */
+	ast_test_validate_cleanup(test, AST_VECTOR_REMOVE_UNORDERED(&sv1, 0) == 1, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_SIZE(&sv1) == 2, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_GET(&sv1, 0) == CCC, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_GET(&sv1, 1) == ZZZ, rc, cleanup);
+
+	/* Replace 0 and 2 leaving 1 alone */
+	ast_test_validate_cleanup(test, AST_VECTOR_REPLACE(&sv1, 0, AAA) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_REPLACE(&sv1, 2, CCC) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_SIZE(&sv1) == 3, 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) == ZZZ, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_GET(&sv1, 2) == CCC, rc, cleanup);
+
+	/* Remove 1 and compact preserving order */
+	ast_test_validate_cleanup(test, AST_VECTOR_REMOVE_ORDERED(&sv1, 1) == ZZZ, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_SIZE(&sv1) == 2, 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) == CCC, rc, cleanup);
+
+	/* Equivalent of APPEND */
+	ast_test_validate_cleanup(test, AST_VECTOR_REPLACE(&sv1, 2, ZZZ) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_SIZE(&sv1) == 3, rc, cleanup);
+
+	/* This should work because we passing in the specific object to be removed */
+	cleanup_count = 0;
+	ast_test_validate_cleanup(test, AST_VECTOR_REMOVE_ELEM_ORDERED(&sv1, ZZZ, cleanup_int) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_SIZE(&sv1) == 2, 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) == CCC, rc, cleanup);
+	ast_test_validate_cleanup(test, cleanup_count == 1, rc, cleanup);
+
+	/* If we want a comparison by value, we need to pass in a comparison
+	 * function.
+	 */
+	cleanup_count = 0;
+	ast_test_validate_cleanup(test, AST_VECTOR_REMOVE_CMP_ORDERED(&sv1, AAA, AST_VECTOR_ELEM_DEFAULT_CMP, cleanup_int) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_SIZE(&sv1) == 1, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_GET(&sv1, 0) == CCC, rc, cleanup);
+	ast_test_validate_cleanup(test, cleanup_count == 1, rc, cleanup);
+
+	/* This element is gone so we shouldn't be able to find it or delete it again. */
+	ast_test_validate_cleanup(test, AST_VECTOR_GET_CMP(&sv1, AAA, AST_VECTOR_ELEM_DEFAULT_CMP) == NULL, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_REMOVE_CMP_ORDERED(&sv1, AAA, AST_VECTOR_ELEM_DEFAULT_CMP, cleanup_int) != 0, rc, cleanup);
+
+	/* CCC should still be there though */
+	ast_test_validate_cleanup(test, *(int *)AST_VECTOR_GET_CMP(&sv1, CCC, AST_VECTOR_ELEM_DEFAULT_CMP) == CCC, rc, cleanup);
+
+cleanup:
+	AST_VECTOR_FREE(&sv1);
+	return rc;
+}
+
+static int visits;
+
+static int cb_match(void *obj, void *arg)
+{
+	visits++;
+	return strcmp(arg, obj) == 0 ? CMP_MATCH : 0;
+}
+
+static int cb_visits(void *obj, int v)
+{
+	visits++;
+	return visits == v ? CMP_STOP : 0;
+}
+
+AST_TEST_DEFINE(callbacks)
+{
+	AST_VECTOR(, char *) sv1;
+	typeof(sv1) *sv2 = NULL;
+
+	int rc = AST_TEST_PASS;
+	char *AAA = "AAA";
+	char *AAA2 = "AAA";
+	char *BBB = "BBB";
+	char *CCC = "CCC";
+	char *DEF = "default_value";
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "callbacks";
+		info->category = "/main/vector/";
+		info->summary = "Test vector callback ops";
+		info->description = "Test vector callback ops";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	AST_VECTOR_INIT(&sv1, 32);
+
+	AST_VECTOR_APPEND(&sv1, AAA);
+	AST_VECTOR_APPEND(&sv1, BBB);
+	AST_VECTOR_APPEND(&sv1, CCC);
+	AST_VECTOR_APPEND(&sv1, AAA2);
+
+	visits = 0;
+	ast_test_validate_cleanup(test, AST_VECTOR_CALLBACK(&sv1, cb_match, DEF, "AAA") == AAA, rc, cleanup);
+	ast_test_validate_cleanup(test, visits == 1, rc, cleanup);
+
+	visits = 0;
+	ast_test_validate_cleanup(test, AST_VECTOR_CALLBACK(&sv1, cb_match, DEF, "XYZ") == DEF, rc, cleanup);
+	ast_test_validate_cleanup(test, visits == 4, rc, cleanup);
+
+	visits = 0;
+	ast_test_validate_cleanup(test, AST_VECTOR_CALLBACK(&sv1, cb_visits, DEF, 2) == DEF, rc, cleanup);
+	ast_test_validate_cleanup(test, visits == 2, rc, cleanup);
+
+
+	sv2 = AST_VECTOR_CALLBACK_MULTIPLE(&sv1, AST_VECTOR_MATCH_ALL);
+	ast_test_validate_cleanup(test, AST_VECTOR_SIZE(sv2) == 4, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_GET(sv2, 0) == AAA, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_GET(sv2, 1) == BBB, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_GET(sv2, 2) == CCC, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_GET(sv2, 3) == AAA2, rc, cleanup);
+
+	AST_VECTOR_PTR_FREE(sv2);
+
+	AST_VECTOR_APPEND(&sv1, AAA);
+	AST_VECTOR_APPEND(&sv1, BBB);
+	AST_VECTOR_APPEND(&sv1, CCC);
+	ast_test_validate_cleanup(test, AST_VECTOR_SIZE(&sv1) == 7, rc, cleanup);
+
+	sv2 = AST_VECTOR_CALLBACK_MULTIPLE(&sv1, cb_match, "AAA");
+	ast_test_validate_cleanup(test, AST_VECTOR_SIZE(sv2) == 3, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_GET(sv2, 0) == AAA, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_GET(sv2, 1) == AAA2, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_GET(sv2, 2) == AAA, rc, cleanup);
+
+cleanup:
+	AST_VECTOR_FREE(&sv1);
+	AST_VECTOR_PTR_FREE(sv2);
+
+	return rc;
+}
+
+AST_TEST_DEFINE(locks)
+{
+	AST_VECTOR_RW(, char *) sv1;
+	int rc = AST_TEST_PASS;
+	struct timespec ts;
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "locks";
+		info->category = "/main/vector/";
+		info->summary = "Test vector locking ops";
+		info->description = "Test vector locking ops";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	/* We're not actually checking that locking works,
+	 * just that the macro expansions work
+	 */
+
+	AST_VECTOR_RW_INIT(&sv1, 0);
+
+	ast_test_validate_cleanup(test, AST_VECTOR_RW_RDLOCK(&sv1) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_RW_UNLOCK(&sv1) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_RW_WRLOCK(&sv1) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_RW_UNLOCK(&sv1) == 0, rc, cleanup);
+
+	ast_test_validate_cleanup(test, AST_VECTOR_RW_RDLOCK_TRY(&sv1) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_RW_WRLOCK_TRY(&sv1) != 0, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_RW_UNLOCK(&sv1) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_RW_WRLOCK_TRY(&sv1) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_RW_UNLOCK(&sv1) == 0, rc, cleanup);
+
+	ts.tv_nsec = 0;
+	ts.tv_sec = 2;
+
+	ast_test_validate_cleanup(test, AST_VECTOR_RW_RDLOCK_TIMED(&sv1, &ts) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_RW_WRLOCK_TIMED(&sv1, &ts) != 0, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_RW_UNLOCK(&sv1) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_RW_WRLOCK_TIMED(&sv1, &ts) == 0, rc, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_RW_UNLOCK(&sv1) == 0, rc, cleanup);
+
+cleanup:
+	AST_VECTOR_RW_FREE(&sv1);
+
+	return rc;
+}
+
+static int unload_module(void)
+{
+	AST_TEST_UNREGISTER(locks);
+	AST_TEST_UNREGISTER(callbacks);
+	AST_TEST_UNREGISTER(basic_ops_integer);
+	AST_TEST_UNREGISTER(basic_ops);
+
+	return 0;
+}
+
+static int load_module(void)
+{
+	AST_TEST_REGISTER(locks);
+	AST_TEST_REGISTER(callbacks);
+	AST_TEST_REGISTER(basic_ops_integer);
+	AST_TEST_REGISTER(basic_ops);
+
+	return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Vector test module");
diff --git a/tests/test_voicemail_api.c b/tests/test_voicemail_api.c
index 9c538c2..aba1b64 100644
--- a/tests/test_voicemail_api.c
+++ b/tests/test_voicemail_api.c
@@ -33,7 +33,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419175 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/stat.h>
 
@@ -940,7 +940,7 @@ AST_TEST_DEFINE(voicemail_api_off_nominal_snapshot)
 			" * Access to non-existent context\n"
 			" * Access to non-existent folder\n"
 			" * Access to NULL folder\n"
-			" * Invalid sort identifier\n";
+			" * Invalid sort identifier";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -1054,7 +1054,7 @@ AST_TEST_DEFINE(voicemail_api_off_nominal_move)
 			" * Moving to a non-existent context\n"
 			" * Moving to/from non-existent folder\n"
 			" * Moving to/from NULL folder\n"
-			" * Invalid message identifier(s)\n";
+			" * Invalid message identifier(s)";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -1173,7 +1173,7 @@ AST_TEST_DEFINE(voicemail_api_off_nominal_remove)
 			" * Removing messages from an invalid context\n"
 			" * Removing messages from an invalid folder\n"
 			" * Removing messages from a NULL folder\n"
-			" * Removing messages with bad identifiers\n";
+			" * Removing messages with bad identifiers";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -1348,7 +1348,7 @@ AST_TEST_DEFINE(voicemail_api_off_nominal_forward)
 			" * Invalid to context\n"
 			" * Invalid/NULL to folder\n"
 			" * Invalid message numbers\n"
-			" * Invalid number of messages\n";
+			" * Invalid number of messages";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -1426,7 +1426,7 @@ AST_TEST_DEFINE(voicemail_api_nominal_msg_playback)
 		info->summary = "Nominal message playback";
 		info->description =
 			"Tests playing back a message on a provided"
-			" channel or callback function\n";
+			" channel or callback function";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
@@ -1488,7 +1488,7 @@ AST_TEST_DEFINE(voicemail_api_off_nominal_msg_playback)
 			" * Invalid/NULL mailbox\n"
 			" * Invalid context\n"
 			" * Invalid/NULL folder\n"
-			" * Invalid message identifiers\n";
+			" * Invalid message identifiers";
 		return AST_TEST_NOT_RUN;
 	case TEST_EXECUTE:
 		break;
diff --git a/tests/test_xml_escape.c b/tests/test_xml_escape.c
index c9f83a6..e4c36de 100644
--- a/tests/test_xml_escape.c
+++ b/tests/test_xml_escape.c
@@ -32,7 +32,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 389251 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/utils.h"
 #include "asterisk/module.h"
diff --git a/utils/Makefile b/utils/Makefile
index 8fb1a29..af21673 100644
--- a/utils/Makefile
+++ b/utils/Makefile
@@ -139,6 +139,9 @@ aelbison.c: $(ASTTOPDIR)/res/ael/ael.tab.c
 	$(ECHO_PREFIX) echo "   [CP] $(subst $(ASTTOPDIR)/,,$<) -> $@"
 	$(CMD_PREFIX) cp "$<" "$@"
 aelbison.o: _ASTCFLAGS+=-I$(ASTTOPDIR)/res/ael -DYYENABLE_NLS=0
+	ifneq ($(AST_CLANG_BLOCKS),)
+		_ASTCFLAGS+=-Wno-parentheses-equality
+	endif
 
 pbx_ael.c: $(ASTTOPDIR)/pbx/pbx_ael.c
 	$(ECHO_PREFIX) echo "   [CP] $(subst $(ASTTOPDIR)/,,$<) -> $@"
diff --git a/utils/ael_main.c b/utils/ael_main.c
index 4438a43..86588ee 100644
--- a/utils/ael_main.c
+++ b/utils/ael_main.c
@@ -18,7 +18,7 @@
 #include <regex.h>
 #include <limits.h>
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 418019 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/backtrace.h"
 #include "asterisk/channel.h"
diff --git a/utils/astdb2sqlite3.c b/utils/astdb2sqlite3.c
index 02103bc..ba35f93 100644
--- a/utils/astdb2sqlite3.c
+++ b/utils/astdb2sqlite3.c
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-//ASTERISK_FILE_VERSION(__FILE__, "$Revision: 369013 $")
+//ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/types.h>
 #include <sys/stat.h>
diff --git a/utils/astman.c b/utils/astman.c
index f03e05e..315b3b0 100644
--- a/utils/astman.c
+++ b/utils/astman.c
@@ -27,7 +27,7 @@
  ***/
 
 #include "asterisk.h"
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 409091 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk.h"
 
 #include <newt.h>
@@ -723,7 +723,7 @@ static int manager_login(char *hostname)
 				MD5Update(&md5, (unsigned char *)pass, strlen(pass));
 				MD5Final(digest, &md5);
 				for (x=0; x<16; x++)
-					len += sprintf(md5key + len, "%2.2x", digest[x]);
+					len += sprintf(md5key + len, "%02hhx", digest[x]);
 				manager_action("Login",
 						"AuthType: MD5\r\n"
 						"Username: %s\r\n"
diff --git a/utils/check_expr.c b/utils/check_expr.c
index f5e2894..6cd535d 100644
--- a/utils/check_expr.c
+++ b/utils/check_expr.c
@@ -21,7 +21,7 @@
  ***/
 
 #include "asterisk.h"
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 398755 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/ast_expr.h"
 
diff --git a/utils/clicompat.c b/utils/clicompat.c
index 0a09d76..d25a710 100644
--- a/utils/clicompat.c
+++ b/utils/clicompat.c
@@ -1,6 +1,6 @@
 /*
  * Stubs for some cli functions used by the test routines.
- * $Revision: 401937 $
+ * $Revision$
  */
 void ast_cli(int fd, const char *fmt, ...);
 void ast_cli(int fd, const char *fmt, ...)
@@ -14,6 +14,13 @@ int ast_register_atexit(void (*func)(void))
 {
 	return 0;
 }
+
+int ast_register_cleanup(void (*func)(void));
+int ast_register_cleanup(void (*func)(void))
+{
+	return 0;
+}
+
 int ast_cli_register_multiple(struct ast_cli_entry *e, int len);
 int ast_cli_register_multiple(struct ast_cli_entry *e, int len)
 {
diff --git a/utils/conf2ael.c b/utils/conf2ael.c
index d789fa1..5d36f42 100644
--- a/utils/conf2ael.c
+++ b/utils/conf2ael.c
@@ -28,7 +28,7 @@
  ***/
 
 #include "asterisk.h"
-ASTERISK_FILE_VERSION(__FILE__, "$Revision: 418019 $")
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/paths.h"	/* CONFIG_DIR */
 #include <locale.h>
@@ -237,19 +237,6 @@ struct store_hint {
 
 AST_LIST_HEAD(store_hints, store_hint);
 
-static const struct cfextension_states {
-	int extension_state;
-	const char * const text;
-} extension_states[] = {
-	{ AST_EXTENSION_NOT_INUSE,                     "Idle" },
-	{ AST_EXTENSION_INUSE,                         "InUse" },
-	{ AST_EXTENSION_BUSY,                          "Busy" },
-	{ AST_EXTENSION_UNAVAILABLE,                   "Unavailable" },
-	{ AST_EXTENSION_RINGING,                       "Ringing" },
-	{ AST_EXTENSION_INUSE | AST_EXTENSION_RINGING, "InUse&Ringing" },
-	{ AST_EXTENSION_ONHOLD,                        "Hold" },
-	{ AST_EXTENSION_INUSE | AST_EXTENSION_ONHOLD,  "InUse&Hold" }
-};
 #define STATUS_NO_CONTEXT	1
 #define STATUS_NO_EXTENSION	2
 #define STATUS_NO_PRIORITY	3
diff --git a/utils/extconf.c b/utils/extconf.c
index 051ce79..7989bcd 100644
--- a/utils/extconf.c
+++ b/utils/extconf.c
@@ -471,151 +471,6 @@ static inline int __ast_pthread_mutex_unlock(const char *filename, int lineno, c
 	return res;
 }
 
-static inline int __ast_cond_init(const char *filename, int lineno, const char *func,
-				  const char *cond_name, ast_cond_t *cond, pthread_condattr_t *cond_attr)
-{
-	return pthread_cond_init(cond, cond_attr);
-}
-
-static inline int __ast_cond_signal(const char *filename, int lineno, const char *func,
-				    const char *cond_name, ast_cond_t *cond)
-{
-	return pthread_cond_signal(cond);
-}
-
-static inline int __ast_cond_broadcast(const char *filename, int lineno, const char *func,
-				       const char *cond_name, ast_cond_t *cond)
-{
-	return pthread_cond_broadcast(cond);
-}
-
-static inline int __ast_cond_destroy(const char *filename, int lineno, const char *func,
-				     const char *cond_name, ast_cond_t *cond)
-{
-	return pthread_cond_destroy(cond);
-}
-
-static inline int __ast_cond_wait(const char *filename, int lineno, const char *func,
-				  const char *cond_name, const char *mutex_name,
-				  ast_cond_t *cond, ast_mutex_t *t)
-{
-	int res;
-	int canlog = strcmp(filename, "logger.c");
-
-#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS
-	if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
-		__ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized.\n",
-				   filename, lineno, func, mutex_name);
-	}
-#endif
-
-	if (t->reentrancy && (t->thread[t->reentrancy-1] != pthread_self())) {
-		__ast_mutex_logger("%s line %d (%s): attempted unlock mutex '%s' without owning it!\n",
-				   filename, lineno, func, mutex_name);
-		__ast_mutex_logger("%s line %d (%s): '%s' was locked here.\n",
-				   t->file[t->reentrancy-1], t->lineno[t->reentrancy-1], t->func[t->reentrancy-1], mutex_name);
-		DO_THREAD_CRASH;
-	}
-
-	if (--t->reentrancy < 0) {
-		__ast_mutex_logger("%s line %d (%s): mutex '%s' freed more times than we've locked!\n",
-				   filename, lineno, func, mutex_name);
-		t->reentrancy = 0;
-	}
-
-	if (t->reentrancy < AST_MAX_REENTRANCY) {
-		t->file[t->reentrancy] = NULL;
-		t->lineno[t->reentrancy] = 0;
-		t->func[t->reentrancy] = NULL;
-		t->thread[t->reentrancy] = 0;
-	}
-
-	if ((res = pthread_cond_wait(cond, &t->mutex))) {
-		__ast_mutex_logger("%s line %d (%s): Error waiting on condition mutex '%s'\n", 
-				   filename, lineno, func, strerror(res));
-		DO_THREAD_CRASH;
-	} else {
-		if (t->reentrancy < AST_MAX_REENTRANCY) {
-			t->file[t->reentrancy] = filename;
-			t->lineno[t->reentrancy] = lineno;
-			t->func[t->reentrancy] = func;
-			t->thread[t->reentrancy] = pthread_self();
-			t->reentrancy++;
-		} else {
-			__ast_mutex_logger("%s line %d (%s): '%s' really deep reentrancy!\n",
-							   filename, lineno, func, mutex_name);
-		}
-	}
-
-	return res;
-}
-
-static inline int __ast_cond_timedwait(const char *filename, int lineno, const char *func,
-				       const char *cond_name, const char *mutex_name, ast_cond_t *cond,
-				       ast_mutex_t *t, const struct timespec *abstime)
-{
-	int res;
-	int canlog = strcmp(filename, "logger.c");
-
-#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS
-	if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
-		__ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized.\n",
-				   filename, lineno, func, mutex_name);
-	}
-#endif
-
-	if (t->reentrancy && (t->thread[t->reentrancy-1] != pthread_self())) {
-		__ast_mutex_logger("%s line %d (%s): attempted unlock mutex '%s' without owning it!\n",
-				   filename, lineno, func, mutex_name);
-		__ast_mutex_logger("%s line %d (%s): '%s' was locked here.\n",
-				   t->file[t->reentrancy-1], t->lineno[t->reentrancy-1], t->func[t->reentrancy-1], mutex_name);
-		DO_THREAD_CRASH;
-	}
-
-	if (--t->reentrancy < 0) {
-		__ast_mutex_logger("%s line %d (%s): mutex '%s' freed more times than we've locked!\n",
-				   filename, lineno, func, mutex_name);
-		t->reentrancy = 0;
-	}
-
-	if (t->reentrancy < AST_MAX_REENTRANCY) {
-		t->file[t->reentrancy] = NULL;
-		t->lineno[t->reentrancy] = 0;
-		t->func[t->reentrancy] = NULL;
-		t->thread[t->reentrancy] = 0;
-	}
-
-	if ((res = pthread_cond_timedwait(cond, &t->mutex, abstime)) && (res != ETIMEDOUT)) {
-		__ast_mutex_logger("%s line %d (%s): Error waiting on condition mutex '%s'\n", 
-				   filename, lineno, func, strerror(res));
-		DO_THREAD_CRASH;
-	} else {
-		if (t->reentrancy < AST_MAX_REENTRANCY) {
-			t->file[t->reentrancy] = filename;
-			t->lineno[t->reentrancy] = lineno;
-			t->func[t->reentrancy] = func;
-			t->thread[t->reentrancy] = pthread_self();
-			t->reentrancy++;
-		} else {
-			__ast_mutex_logger("%s line %d (%s): '%s' really deep reentrancy!\n",
-							   filename, lineno, func, mutex_name);
-		}
-	}
-
-	return res;
-}
-
-#define ast_mutex_destroy(a) __ast_pthread_mutex_destroy(__FILE__, __LINE__, __PRETTY_FUNCTION__, #a, a)
-#define ast_mutex_lock(a) __ast_pthread_mutex_lock(__FILE__, __LINE__, __PRETTY_FUNCTION__, #a, a)
-#define ast_mutex_unlock(a) __ast_pthread_mutex_unlock(__FILE__, __LINE__, __PRETTY_FUNCTION__, #a, a)
-#define ast_mutex_trylock(a) __ast_pthread_mutex_trylock(__FILE__, __LINE__, __PRETTY_FUNCTION__, #a, a)
-#define ast_cond_init(cond, attr) __ast_cond_init(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, cond, attr)
-#define ast_cond_destroy(cond) __ast_cond_destroy(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, cond)
-#define ast_cond_signal(cond) __ast_cond_signal(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, cond)
-#define ast_cond_broadcast(cond) __ast_cond_broadcast(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, cond)
-#define ast_cond_wait(cond, mutex) __ast_cond_wait(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, #mutex, cond, mutex)
-#define ast_cond_timedwait(cond, mutex, time) __ast_cond_timedwait(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, #mutex, cond, mutex, time)
-
 #else /* !DEBUG_THREADS */
 
 
@@ -635,58 +490,8 @@ static inline int ast_mutex_init(ast_mutex_t *pmutex)
 
 #define ast_pthread_mutex_init(pmutex,a) pthread_mutex_init(pmutex,a)
 
-static inline int ast_mutex_unlock(ast_mutex_t *pmutex)
-{
-	return pthread_mutex_unlock(pmutex);
-}
-
-static inline int ast_mutex_destroy(ast_mutex_t *pmutex)
-{
-	return pthread_mutex_destroy(pmutex);
-}
-
-static inline int ast_mutex_lock(ast_mutex_t *pmutex)
-{
-	__MTX_PROF(pmutex);
-}
-
-static inline int ast_mutex_trylock(ast_mutex_t *pmutex)
-{
-	return pthread_mutex_trylock(pmutex);
-}
-
 typedef pthread_cond_t ast_cond_t;
 
-static inline int ast_cond_init(ast_cond_t *cond, pthread_condattr_t *cond_attr)
-{
-	return pthread_cond_init(cond, cond_attr);
-}
-
-static inline int ast_cond_signal(ast_cond_t *cond)
-{
-	return pthread_cond_signal(cond);
-}
-
-static inline int ast_cond_broadcast(ast_cond_t *cond)
-{
-	return pthread_cond_broadcast(cond);
-}
-
-static inline int ast_cond_destroy(ast_cond_t *cond)
-{
-	return pthread_cond_destroy(cond);
-}
-
-static inline int ast_cond_wait(ast_cond_t *cond, ast_mutex_t *t)
-{
-	return pthread_cond_wait(cond, t);
-}
-
-static inline int ast_cond_timedwait(ast_cond_t *cond, ast_mutex_t *t, const struct timespec *abstime)
-{
-	return pthread_cond_timedwait(cond, t, abstime);
-}
-
 #endif /* !DEBUG_THREADS */
 
 #if defined(AST_MUTEX_INIT_W_CONSTRUCTORS)
@@ -697,10 +502,6 @@ static inline int ast_cond_timedwait(ast_cond_t *cond, ast_mutex_t *t, const str
 static void  __attribute__((constructor)) init_##mutex(void) \
 { \
 	ast_mutex_init(&mutex); \
-} \
-static void  __attribute__((destructor)) fini_##mutex(void) \
-{ \
-	ast_mutex_destroy(&mutex); \
 }
 #else /* !AST_MUTEX_INIT_W_CONSTRUCTORS */
 /* By default, use static initialization of mutexes. */ 
@@ -709,18 +510,8 @@ static void  __attribute__((destructor)) fini_##mutex(void) \
 #endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
 
 #define pthread_mutex_t use_ast_mutex_t_instead_of_pthread_mutex_t
-#define pthread_mutex_lock use_ast_mutex_lock_instead_of_pthread_mutex_lock
-#define pthread_mutex_unlock use_ast_mutex_unlock_instead_of_pthread_mutex_unlock
-#define pthread_mutex_trylock use_ast_mutex_trylock_instead_of_pthread_mutex_trylock
 #define pthread_mutex_init use_ast_mutex_init_instead_of_pthread_mutex_init
-#define pthread_mutex_destroy use_ast_mutex_destroy_instead_of_pthread_mutex_destroy
 #define pthread_cond_t use_ast_cond_t_instead_of_pthread_cond_t
-#define pthread_cond_init use_ast_cond_init_instead_of_pthread_cond_init
-#define pthread_cond_destroy use_ast_cond_destroy_instead_of_pthread_cond_destroy
-#define pthread_cond_signal use_ast_cond_signal_instead_of_pthread_cond_signal
-#define pthread_cond_broadcast use_ast_cond_broadcast_instead_of_pthread_cond_broadcast
-#define pthread_cond_wait use_ast_cond_wait_instead_of_pthread_cond_wait
-#define pthread_cond_timedwait use_ast_cond_timedwait_instead_of_pthread_cond_timedwait
 
 #define AST_MUTEX_DEFINE_STATIC(mutex) __AST_MUTEX_DEFINE(static, mutex)
 
@@ -762,21 +553,11 @@ static inline int ast_rwlock_rdlock(ast_rwlock_t *prwlock)
 	return pthread_rwlock_rdlock(prwlock);
 }
 
-static inline int ast_rwlock_tryrdlock(ast_rwlock_t *prwlock)
-{
-	return pthread_rwlock_tryrdlock(prwlock);
-}
-
 static inline int ast_rwlock_wrlock(ast_rwlock_t *prwlock)
 {
 	return pthread_rwlock_wrlock(prwlock);
 }
 
-static inline int ast_rwlock_trywrlock(ast_rwlock_t *prwlock)
-{
-	return pthread_rwlock_trywrlock(prwlock);
-}
-
 /* Statically declared read/write locks */
 
 #ifndef HAVE_PTHREAD_RWLOCK_INITIALIZER
@@ -880,18 +661,7 @@ AST_INLINE_API(int ast_atomic_dec_and_test(volatile int *p),
 })
 #endif
 
-#ifndef DEBUG_CHANNEL_LOCKS
-/*! \brief Lock a channel. If DEBUG_CHANNEL_LOCKS is defined 
-	in the Makefile, print relevant output for debugging */
-#define ast_channel_lock(x)		ast_mutex_lock(&x->lock)
-/*! \brief Unlock a channel. If DEBUG_CHANNEL_LOCKS is defined 
-	in the Makefile, print relevant output for debugging */
-#define ast_channel_unlock(x)		ast_mutex_unlock(&x->lock)
-/*! \brief Try locking a channel. If DEBUG_CHANNEL_LOCKS is defined 
-	in the Makefile, print relevant output for debugging */
-#define ast_channel_trylock(x)		ast_mutex_trylock(&x->lock)
-#else
-
+#ifdef DEBUG_CHANNEL_LOCKS
 /*! \brief Lock AST channel (and print debugging output)
 \note You need to enable DEBUG_CHANNEL_LOCKS for this function */
 int ast_channel_lock(struct ast_channel *chan);
@@ -951,22 +721,19 @@ int ast_channel_trylock(struct ast_channel *chan);
 #define ast_vasprintf(ret, fmt, ap) \
 	_ast_vasprintf((ret), __FILE__, __LINE__, __PRETTY_FUNCTION__, (fmt), (ap))
 
-
-static unsigned int __unsigned_int_flags_dummy;
-
 struct ast_flags {  /* stolen from utils.h */
 	unsigned int flags;
 };
 #define ast_test_flag(p,flag) 		({ \
 					typeof ((p)->flags) __p = (p)->flags; \
-					typeof (__unsigned_int_flags_dummy) __x = 0; \
+					unsigned int __x = 0; \
 					(void) (&__p == &__x); \
 					((p)->flags & (flag)); \
 					})
 
 #define ast_set2_flag(p,value,flag)	do { \
 					typeof ((p)->flags) __p = (p)->flags; \
-					typeof (__unsigned_int_flags_dummy) __x = 0; \
+					unsigned int __x = 0; \
 					(void) (&__p == &__x); \
 					if (value) \
 						(p)->flags |= (flag); \
@@ -1906,9 +1673,6 @@ extern int ast_language_is_prefix;
 
 /* linkedlists.h */
 
-#define AST_LIST_LOCK(head)						\
-	ast_mutex_lock(&(head)->lock) 
-
 /*!
   \brief Write locks a list.
   \param head This is a pointer to the list head structure
@@ -1932,50 +1696,6 @@ extern int ast_language_is_prefix;
         ast_rwlock_rdlock(&(head)->lock)
 	
 /*!
-  \brief Locks a list, without blocking if the list is locked.
-  \param head This is a pointer to the list head structure
-
-  This macro attempts to place an exclusive lock in the
-  list head structure pointed to by head.
-  Returns non-zero on success, 0 on failure
-*/
-#define AST_LIST_TRYLOCK(head)						\
-	ast_mutex_trylock(&(head)->lock) 
-
-/*!
-  \brief Write locks a list, without blocking if the list is locked.
-  \param head This is a pointer to the list head structure
-
-  This macro attempts to place an exclusive write lock in the
-  list head structure pointed to by head.
-  Returns non-zero on success, 0 on failure
-*/
-#define AST_RWLIST_TRYWRLOCK(head)                                      \
-        ast_rwlock_trywrlock(&(head)->lock)
-
-/*!
-  \brief Read locks a list, without blocking if the list is locked.
-  \param head This is a pointer to the list head structure
-
-  This macro attempts to place a read lock in the
-  list head structure pointed to by head.
-  Returns non-zero on success, 0 on failure
-*/
-#define AST_RWLIST_TRYRDLOCK(head)                                      \
-        ast_rwlock_tryrdlock(&(head)->lock)
-	
-/*!
-  \brief Attempts to unlock a list.
-  \param head This is a pointer to the list head structure
-
-  This macro attempts to remove an exclusive lock from the
-  list head structure pointed to by head. If the list
-  was not locked by this thread, this macro has no effect.
-*/
-#define AST_LIST_UNLOCK(head) 						\
-	ast_mutex_unlock(&(head)->lock)
-
-/*!
   \brief Attempts to unlock a read/write based list.
   \param head This is a pointer to the list head structure
 
@@ -2453,20 +2173,6 @@ struct {								\
 }
 
 /*!
-  \brief Destroys a list head structure.
-  \param head This is a pointer to the list head structure
-
-  This macro destroys a list head structure by setting the head
-  entry to \a NULL (empty list) and destroying the embedded lock.
-  It does not free the structure from memory.
-*/
-#define AST_LIST_HEAD_DESTROY(head) {					\
-	(head)->first = NULL;						\
-	(head)->last = NULL;						\
-	ast_mutex_destroy(&(head)->lock);				\
-}
-
-/*!
   \brief Destroys an rwlist head structure.
   \param head This is a pointer to the list head structure
 
@@ -3011,48 +2717,12 @@ struct store_hint {
 
 AST_LIST_HEAD(store_hints, store_hint);
 
-static const struct cfextension_states {
-	int extension_state;
-	const char * const text;
-} extension_states[] = {
-	{ AST_EXTENSION_NOT_INUSE,                     "Idle" },
-	{ AST_EXTENSION_INUSE,                         "InUse" },
-	{ AST_EXTENSION_BUSY,                          "Busy" },
-	{ AST_EXTENSION_UNAVAILABLE,                   "Unavailable" },
-	{ AST_EXTENSION_RINGING,                       "Ringing" },
-	{ AST_EXTENSION_INUSE | AST_EXTENSION_RINGING, "InUse&Ringing" },
-	{ AST_EXTENSION_ONHOLD,                        "Hold" },
-	{ AST_EXTENSION_INUSE | AST_EXTENSION_ONHOLD,  "InUse&Hold" }
-};
 #define STATUS_NO_CONTEXT	1
 #define STATUS_NO_EXTENSION	2
 #define STATUS_NO_PRIORITY	3
 #define STATUS_NO_LABEL		4
 #define STATUS_SUCCESS		5
 
-
-#if defined ( __i386__) && (defined(__FreeBSD__) || defined(linux))
-#if defined(__FreeBSD__)
-#include <machine/cpufunc.h>
-#elif defined(linux)
-static __inline uint64_t
-rdtsc(void)
-{ 
-	uint64_t rv;
-
-	__asm __volatile(".byte 0x0f, 0x31" : "=A" (rv));
-	return (rv);
-}
-#endif
-#else	/* supply a dummy function on other platforms */
-static __inline uint64_t
-rdtsc(void)
-{
-	return 0;
-}
-#endif
-
-
 static struct ast_var_t *ast_var_assign(const char *name, const char *value)
 {	
 	struct ast_var_t *var;
diff --git a/utils/smsq.c b/utils/smsq.c
index fab32f0..21ab80d 100644
--- a/utils/smsq.c
+++ b/utils/smsq.c
@@ -390,14 +390,14 @@ static void rxqcheck (char *dir, char *queue, char *process)
             {
                for (n = 0, x = 0; x < udl; x++)
                {
-                  sprintf (tmp + n, "%02X", ud[x]);
+                  sprintf (tmp + n, "%02hX", ud[x]);
                   n += 2;
                }
                setenv ("ud8", tmp, 1);
             }
             for (n = 0, x = 0; x < udl; x++)
             {
-               sprintf (tmp + n, "%04X", ud[x]);
+               sprintf (tmp + n, "%04hX", ud[x]);
                n += 4;
             }
             setenv ("ud16", tmp, 1);
@@ -730,13 +730,13 @@ main (int argc, const char *argv[])
             {                   /* use one byte hex */
                fprintf (f, "ud#");
                for (p = 0; p < udl; p++)
-                  fprintf (f, "%02X", ud[p]);
+                  fprintf (f, "%02hX", ud[p]);
             }
          } else
          {                      /* use two byte hex */
             fprintf (f, "ud##");
             for (p = 0; p < udl; p++)
-               fprintf (f, "%04X", ud[p]);
+               fprintf (f, "%04hX", ud[p]);
          }
          fprintf (f, "\n");
       }

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