summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES80
-rw-r--r--Makefile6
-rw-r--r--Makefile.moddir_rules2
-rw-r--r--Makefile.rules4
-rw-r--r--addons/chan_ooh323.c48
-rw-r--r--apps/app_dial.c126
-rw-r--r--apps/app_queue.c28
-rw-r--r--apps/app_userevent.c4
-rw-r--r--apps/app_voicemail.c26
-rw-r--r--autoconf/acx_pthread.m4243
-rw-r--r--autoconf/ast_ext_lib.m42
-rw-r--r--autoconf/ast_prog_ld.m42
-rw-r--r--autoconf/ax_pthread.m4485
-rw-r--r--autoconf/libcurl.m449
-rw-r--r--channels/chan_dahdi.c97
-rw-r--r--channels/chan_dahdi.h5
-rw-r--r--channels/chan_iax2.c19
-rw-r--r--channels/chan_pjsip.c71
-rw-r--r--channels/chan_sip.c213
-rw-r--r--channels/chan_unistim.c4
-rw-r--r--channels/pjsip/dialplan_functions.c129
-rw-r--r--channels/pjsip/include/dialplan_functions.h12
-rw-r--r--channels/sip/config_parser.c7
-rw-r--r--channels/sip/dialplan_functions.c82
-rw-r--r--channels/sip/reqresp_parser.c23
-rw-r--r--configs/basic-pbx/modules.conf4
-rw-r--r--configs/samples/asterisk.conf.sample8
-rw-r--r--configs/samples/chan_dahdi.conf.sample9
-rw-r--r--configs/samples/dsp.conf.sample28
-rw-r--r--configs/samples/hep.conf.sample2
-rw-r--r--configs/samples/manager.conf.sample4
-rw-r--r--configs/samples/pjsip.conf.sample43
-rw-r--r--configs/samples/voicemail.conf.sample10
-rwxr-xr-xconfigure924
-rw-r--r--configure.ac27
-rw-r--r--contrib/ast-db-manage/config/versions/3772f8f828da_update_identify_by.py44
-rw-r--r--contrib/ast-db-manage/config/versions/4a6c67fa9b7a_add_fax_detect_timeout_option.py23
-rwxr-xr-xcontrib/ast-db-manage/config/versions/581a4264e537_adding_extensions.py10
-rw-r--r--contrib/ast-db-manage/config/versions/81b01a191a46_pjsip_add_contact_reg_server.py2
-rw-r--r--contrib/ast-db-manage/config/versions/9deac0ae4717_pjsip_add_subscribe_context.py21
-rw-r--r--contrib/ast-db-manage/config/versions/c7a44a5a0851_pjsip_add_global_mwi_options.py35
-rw-r--r--contrib/ast-db-manage/config/versions/ef7efc2d3964_ps_contacts_add_endpoint_and_modify_.py33
-rwxr-xr-xcontrib/scripts/install_prereq4
-rw-r--r--contrib/scripts/sip_to_pjsip/astconfigparser.py27
-rw-r--r--doc/appdocsxml.dtd2
-rw-r--r--funcs/func_cdr.c10
-rw-r--r--funcs/func_channel.c218
-rw-r--r--funcs/func_curl.c2
-rw-r--r--funcs/func_env.c4
-rw-r--r--funcs/func_odbc.c16
-rw-r--r--include/asterisk.h7
-rw-r--r--include/asterisk/_private.h2
-rw-r--r--include/asterisk/autoconfig.h.in27
-rw-r--r--include/asterisk/codec.h2
-rw-r--r--include/asterisk/compiler.h6
-rw-r--r--include/asterisk/config_options.h22
-rw-r--r--include/asterisk/event_defs.h13
-rw-r--r--include/asterisk/format_cache.h8
-rw-r--r--include/asterisk/pbx.h12
-rw-r--r--include/asterisk/res_fax.h4
-rw-r--r--include/asterisk/res_odbc.h5
-rw-r--r--include/asterisk/res_pjsip.h49
-rw-r--r--include/asterisk/res_pjsip_session.h2
-rw-r--r--include/asterisk/stasis_app.h9
-rw-r--r--include/asterisk/stasis_system.h6
-rw-r--r--include/asterisk/taskprocessor.h32
-rw-r--r--include/asterisk/utils.h17
-rw-r--r--main/aoc.c14
-rw-r--r--main/ast_expr2.c11
-rw-r--r--main/ast_expr2.y11
-rw-r--r--main/asterisk.c381
-rw-r--r--main/astobj2_hash.c4
-rw-r--r--main/astobj2_rbtree.c2
-rw-r--r--main/bridge.c12
-rw-r--r--main/bridge_channel.c9
-rw-r--r--main/channel.c41
-rw-r--r--main/codec.c62
-rw-r--r--main/codec_builtin.c72
-rw-r--r--main/config_options.c5
-rw-r--r--main/dsp.c504
-rw-r--r--main/features.c21
-rw-r--r--main/format_cache.c20
-rw-r--r--main/http.c2
-rw-r--r--main/manager.c91
-rw-r--r--main/manager_bridges.c46
-rw-r--r--main/manager_channels.c56
-rw-r--r--main/named_locks.c4
-rw-r--r--main/pbx.c233
-rw-r--r--main/pbx_functions.c19
-rw-r--r--main/rtp_engine.c20
-rw-r--r--main/say.c20
-rw-r--r--main/sorcery.c7
-rw-r--r--main/stasis.c1
-rw-r--r--main/stasis_bridges.c6
-rw-r--r--main/stasis_endpoints.c34
-rw-r--r--main/stasis_system.c6
-rw-r--r--main/stdtime/localtime.c11
-rw-r--r--main/taskprocessor.c37
-rw-r--r--main/threadpool.c6
-rw-r--r--main/translate.c2
-rw-r--r--main/utils.c16
-rw-r--r--menuselect/menuselect.c24
-rw-r--r--menuselect/menuselect.h17
-rw-r--r--menuselect/menuselect_curses.c61
-rw-r--r--menuselect/menuselect_gtk.c11
-rw-r--r--menuselect/menuselect_newt.c2
-rw-r--r--pbx/pbx_dundi.c3
-rw-r--r--res/ael/pval.c70
-rw-r--r--res/ari/resource_bridges.c9
-rw-r--r--res/ari/resource_channels.h4
-rw-r--r--res/res_agi.c384
-rw-r--r--res/res_config_odbc.c1
-rw-r--r--res/res_corosync.c279
-rw-r--r--res/res_fax.c65
-rw-r--r--res/res_format_attr_silk.c64
-rw-r--r--res/res_format_attr_siren14.c94
-rw-r--r--res/res_format_attr_siren7.c94
-rw-r--r--res/res_odbc.c16
-rw-r--r--res/res_phoneprov.c5
-rw-r--r--res/res_pjproject.c7
-rw-r--r--res/res_pjsip.c150
-rw-r--r--res/res_pjsip/config_global.c80
-rw-r--r--res/res_pjsip/config_transport.c3
-rw-r--r--res/res_pjsip/location.c148
-rw-r--r--res/res_pjsip/pjsip_configuration.c221
-rw-r--r--res/res_pjsip/pjsip_distributor.c3
-rw-r--r--res/res_pjsip/pjsip_options.c209
-rw-r--r--res/res_pjsip_caller_id.c2
-rw-r--r--res/res_pjsip_diversion.c2
-rw-r--r--res/res_pjsip_exten_state.c10
-rw-r--r--res/res_pjsip_messaging.c18
-rw-r--r--res/res_pjsip_mwi.c188
-rw-r--r--res/res_pjsip_outbound_publish.c131
-rw-r--r--res/res_pjsip_publish_asterisk.c5
-rw-r--r--res/res_pjsip_pubsub.c315
-rw-r--r--res/res_pjsip_refer.c3
-rw-r--r--res/res_pjsip_registrar.c4
-rw-r--r--res/res_pjsip_registrar_expire.c4
-rw-r--r--res/res_pjsip_sdp_rtp.c2
-rw-r--r--res/res_pjsip_session.c170
-rw-r--r--res/res_pjsip_t38.c4
-rw-r--r--res/res_pjsip_transport_management.c100
-rw-r--r--res/res_rtp_asterisk.c129
-rw-r--r--res/res_sorcery_config.c4
-rw-r--r--res/res_sorcery_memory.c4
-rw-r--r--res/res_sorcery_realtime.c2
-rw-r--r--res/res_stasis.c16
-rw-r--r--res/res_xmpp.c4
-rw-r--r--tests/test_res_pjsip_scheduler.c1
-rw-r--r--tests/test_sorcery_realtime.c37
-rw-r--r--third-party/pjproject/Makefile.rules31
-rw-r--r--third-party/pjproject/configure.m41
-rw-r--r--third-party/pjproject/patches/0001-r5397-pjsip_generic_array_max_count.patch58
-rw-r--r--third-party/pjproject/patches/0001-r5400-pjsip_tx_data_dec_ref.patch24
-rw-r--r--third-party/pjproject/patches/0001-sip_transport_tcp-tls-Set-factory-on-transports-crea.patch48
-rw-r--r--third-party/pjproject/patches/0002-Fix-1946-Avoid-deinitialization-of-uninitialized-cli.patch56
-rw-r--r--third-party/pjproject/patches/config_site.h6
-rw-r--r--third-party/versions.mak2
158 files changed, 6498 insertions, 2193 deletions
diff --git a/CHANGES b/CHANGES
index 55ec3292a..95354791b 100644
--- a/CHANGES
+++ b/CHANGES
@@ -9,6 +9,84 @@
==============================================================================
------------------------------------------------------------------------------
+--- Functionality changes from Asterisk 13.11.0 to Asterisk 13.12.0 ----------
+------------------------------------------------------------------------------
+
+app_voicemail
+------------------
+ * Added "tps_queue_high" and "tps_queue_low" options.
+ The options can modify the taskprocessor alert levels for this module.
+ Additional information can be found in the sample configuration file at
+ config/samples/voicemail.conf.sample.
+
+res_pjsip_mwi
+------------------
+ * Added "mwi_tps_queue_high" and "mwi_tps_queue_low" global configuration
+ options to tune taskprocessor alert levels.
+
+ * Added "mwi_disable_initial_unsolicited" global configuration option
+ to disable sending unsolicited MWI to all endpoints on startup.
+ Additional information can be found in the sample configuration file at
+ config/samples/pjsip.conf.sample.
+
+chan_pjsip
+------------------
+ * A new dialplan function, PJSIP_SEND_SESSION_REFRESH, has been added. When
+ invoked, a re-INVITE or UPDATE request will be sent immediately to the
+ endpoint underlying the channel. When used in combination with the existing
+ dialplan function PJSIP_MEDIA_OFFER, this allows the formats on a PJSIP
+ channel to be re-negotiated and updated after session set up.
+
+------------------------------------------------------------------------------
+--- Functionality changes from Asterisk 13.10.0 to Asterisk 13.11.0 ----------
+------------------------------------------------------------------------------
+
+chan_dahdi
+------------------
+ * Added "faxdetect_timeout" option.
+ The option determines how many seconds into a call before faxdetect
+ is disabled for the call. Setting the value to zero disables the timeout.
+
+chan_sip
+------------------
+ * Session-Timers (RFC 4028) work for TCP (and TLS) transports as well now.
+ Previously Asterisk dropped calls only with UDP transports. However with
+ longer international calls via TCP, the SIP channel might break, because
+ all hops on the Internet route must stay online (have not a single power
+ outage, for example). Therefore with Session-Timers enabled (which are
+ enabled at default), you might see additional dropped calls. Consequently
+ please, consider to go for session-timers=refuse in your sip.conf.
+
+res_pjsip
+------------------
+ * Added "fax_detect_timeout" to endpoint.
+ The option determines how many seconds into a call before fax_detect
+ is disabled for the call. Setting the value to zero disables the timeout.
+
+ * Added "subscribe_context" to endpoint.
+ If specified, incoming SUBSCRIBE requests will be searched for the matching
+ extension in the indicated context. If no "subscribe_context" is specified,
+ then the "context" setting is used.
+
+res_rtp_asterisk
+------------------
+ * The DTLS part in Asterisk now supports Perfect Forward Secrecy (PFS).
+ Enabling PFS is attempted by default, and is dependent on the configuration
+ of the module using TLS.
+ - Ephemeral ECDH (ECDHE) is enabled by default. To disable it, do not
+ specify a ECDHE cipher suite in sip.conf, for example:
+ dtlscipher=AES128-SHA
+ - Ephemeral DH (DHE) is disabled by default. To enable it, add DH parameters
+ into the private key file, e.g., sip.conf dtlsprivatekey. For example:
+ openssl dhparam -out ./dh.pem 2048
+ - Because clients expect the server to prefer PFS, and because OpenSSL sorts
+ its cipher suites by bit strength, see "openssl ciphers -v DEFAULT".
+ Consider re-ordering your cipher suites in the respective configuration
+ file. For example:
+ dtlscipher=ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256
+ which forces PFS and requires at least DTLS 1.2.
+
+------------------------------------------------------------------------------
--- Functionality changes from Asterisk 13.9.0 to Asterisk 13.10.0 -----------
------------------------------------------------------------------------------
@@ -75,8 +153,6 @@ res_pjsip
"contact_deny" - List of Contact header addresses to deny
"contact_permit" - List of Contact header addresses to permit
- * Added new status Updated to AMI event ContactStatus on update registration
-
* Added "reg_server" to contacts.
If the Asterisk system name is set in asterisk.conf, it will be stored
into the "reg_server" field in the ps_contacts table to facilitate
diff --git a/Makefile b/Makefile
index 20b778762..6385fac9f 100644
--- a/Makefile
+++ b/Makefile
@@ -492,7 +492,7 @@ doc/core-en_US.xml: makeopts .lastclean $(XML_core_en_US)
@printf "Building Documentation For: "
@echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" > $@
@echo "<!DOCTYPE docs SYSTEM \"appdocsxml.dtd\">" >> $@
- @echo "<?xml-stylesheet type=\"text/xsl\" href=\"appdocsxml.xslt\"?>" > $@
+ @echo "<?xml-stylesheet type=\"text/xsl\" href=\"appdocsxml.xslt\"?>" >> $@
@echo "<docs xmlns:xi=\"http://www.w3.org/2001/XInclude\">" >> $@
@for x in $(MOD_SUBDIRS); do \
printf "$$x " ; \
@@ -516,7 +516,7 @@ else
@printf "Building Documentation For: "
@echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" > $@
@echo "<!DOCTYPE docs SYSTEM \"appdocsxml.dtd\">" >> $@
- @echo "<?xml-stylesheet type=\"text/xsl\" href=\"appdocsxml.xslt\"?>" > $@
+ @echo "<?xml-stylesheet type=\"text/xsl\" href=\"appdocsxml.xslt\"?>" >> $@
@echo "<docs xmlns:xi=\"http://www.w3.org/2001/XInclude\">" >> $@
@for x in $(MOD_SUBDIRS); do \
printf "$$x " ; \
@@ -825,7 +825,7 @@ install-logrotate:
rm -f contrib/scripts/asterisk.logrotate.tmp
config:
- if [ -f /etc/redhat-release -o -f /etc/fedora-release ]; then \
+ @if [ -f /etc/redhat-release -o -f /etc/fedora-release ]; then \
./build_tools/install_subst contrib/init.d/rc.redhat.asterisk "$(DESTDIR)/etc/rc.d/init.d/asterisk"; \
if [ ! -f "$(DESTDIR)/etc/sysconfig/asterisk" ] ; then \
$(INSTALL) -m 644 contrib/init.d/etc_default_asterisk "$(DESTDIR)/etc/sysconfig/asterisk" ; \
diff --git a/Makefile.moddir_rules b/Makefile.moddir_rules
index 7f1c8c843..d508606db 100644
--- a/Makefile.moddir_rules
+++ b/Makefile.moddir_rules
@@ -142,7 +142,7 @@ dist-clean::
$(AWK) -f $(ASTTOPDIR)/build_tools/get_moduleinfo $^ >> $@
echo "</member>" >> $@
-.moduleinfo:: $(addsuffix .moduleinfo,$(addprefix .,$(sort $(ALL_C_MODS) $(ALL_CC_MODS))))
+.moduleinfo:: $(addsuffix .moduleinfo,$(addprefix .,$(sort $(ALL_C_MODS) $(ALL_CC_MODS)))) $(wildcard $(call tolower,$(MENUSELECT_CATEGORY)).xml)
@echo "<category name=\"MENUSELECT_$(MENUSELECT_CATEGORY)\" displayname=\"$(MENUSELECT_DESCRIPTION)\" remove_on_change=\"$(SUBDIR)/modules.link\">" > $@
@cat $^ >> $@
@echo "</category>" >> $@
diff --git a/Makefile.rules b/Makefile.rules
index a22f19c16..45989d684 100644
--- a/Makefile.rules
+++ b/Makefile.rules
@@ -17,6 +17,10 @@
-include $(ASTTOPDIR)/makeopts
+# Helpful functions
+# call with $(call function,...)
+tolower = $(shell echo $(1) | tr '[:upper:]' '[:lower:]')
+
.PHONY: dist-clean
# If 'make' decides to create intermediate files to satisfy a build requirement
diff --git a/addons/chan_ooh323.c b/addons/chan_ooh323.c
index b5306973c..9ab90aed8 100644
--- a/addons/chan_ooh323.c
+++ b/addons/chan_ooh323.c
@@ -22,6 +22,54 @@
<support_level>extended</support_level>
***/
+/*** DOCUMENTATION
+<info name="OOH323CHANNEL" language="en_US" tech="OOH323">
+ <enumlist>
+ <enum name="faxdetect">
+ <para>R/W Fax Detect</para>
+ <para>Returns 0 or 1</para>
+ <para>Write yes or no</para>
+ </enum>
+ <enum name="t38support">
+ <para>R/W t38support</para>
+ <para>Returns 0 or 1</para>
+ <para>Write yes or no</para>
+ </enum>
+ <enum name="h323id_url">
+ <para>R/0 Returns caller URL</para>
+ </enum>
+ <enum name="caller_h323id">
+ <para>R/0 Returns caller h323id</para>
+ </enum>
+ <enum name="caller_dialeddigits">
+ <para>R/0 Returns caller dialed digits</para>
+ </enum>
+ <enum name="caller_email">
+ <para>R/0 Returns caller email</para>
+ </enum>
+ <enum name="callee_email">
+ <para>R/0 Returns callee email</para>
+ </enum>
+ <enum name="callee_dialeddigits">
+ <para>R/0 Returns callee dialed digits</para>
+ </enum>
+ <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>
+</info>
+ ***/
+
#include "chan_ooh323.h"
#include <math.h>
diff --git a/apps/app_dial.c b/apps/app_dial.c
index c05aecaf9..c2484a2f3 100644
--- a/apps/app_dial.c
+++ b/apps/app_dial.c
@@ -103,15 +103,16 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<option name="a">
<para>Immediately answer the calling channel when the called channel answers in
all cases. Normally, the calling channel is answered when the called channel
- answers, but when options such as A() and M() are used, the calling channel is
+ answers, but when options such as <literal>A()</literal> and
+ <literal>M()</literal> are used, the calling channel is
not answered until all actions on the called channel (such as playing an
announcement) are completed. This option can be used to answer the calling
channel before doing anything on the called channel. You will rarely need to use
this option, the default behavior is adequate in most cases.</para>
</option>
<option name="b" argsep="^">
- <para>Before initiating an outgoing call, Gosub to the specified
- location using the newly created channel. The Gosub will be
+ <para>Before initiating an outgoing call, <literal>Gosub</literal> to the specified
+ location using the newly created channel. The <literal>Gosub</literal> will be
executed for each destination channel.</para>
<argument name="context" required="false" />
<argument name="exten" required="false" />
@@ -121,8 +122,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</argument>
</option>
<option name="B" argsep="^">
- <para>Before initiating the outgoing call(s), Gosub to the specified
- location using the current channel.</para>
+ <para>Before initiating the outgoing call(s), <literal>Gosub</literal> to the
+ specified location using the current channel.</para>
<argument name="context" required="false" />
<argument name="exten" required="false" />
<argument name="priority" required="true" hasparams="optional" argsep="^">
@@ -134,7 +135,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>Reset the call detail record (CDR) for this call.</para>
</option>
<option name="c">
- <para>If the Dial() application cancels this call, always set HANGUPCAUSE to 'answered elsewhere'</para>
+ <para>If the Dial() application cancels this call, always set
+ <variable>HANGUPCAUSE</variable> to 'answered elsewhere'</para>
</option>
<option name="d">
<para>Allow the calling user to dial a 1 digit extension while waiting for
@@ -156,8 +158,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<replaceable>called</replaceable> DTMF string is sent to the called party, and the
<replaceable>calling</replaceable> DTMF string is sent to the calling party. Both arguments
can be used alone. If <replaceable>progress</replaceable> is specified, its DTMF is sent
- to the called party immediately after receiving a PROGRESS message.</para>
- <para>See SendDTMF for valid digits.</para>
+ to the called party immediately after receiving a <literal>PROGRESS</literal> message.</para>
+ <para>See <literal>SendDTMF</literal> for valid digits.</para>
</option>
<option name="e">
<para>Execute the <literal>h</literal> extension for peer after the call ends</para>
@@ -165,7 +167,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<option name="f">
<argument name="x" required="false" />
<para>If <replaceable>x</replaceable> is not provided, force the CallerID sent on a call-forward or
- deflection to the dialplan extension of this Dial() using a dialplan <literal>hint</literal>.
+ deflection to the dialplan extension of this <literal>Dial()</literal> using a dialplan <literal>hint</literal>.
For example, some PSTNs do not allow CallerID to be set to anything
other than the numbers assigned to you.
If <replaceable>x</replaceable> is provided, force the CallerID sent to <replaceable>x</replaceable>.</para>
@@ -318,11 +320,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<note>
<para>You cannot use any additional action post answer options in conjunction
with this option. Also, pbx services are run on the peer (called) channel,
- so you will not be able to set timeouts via the TIMEOUT() function in this macro.</para>
+ so you will not be able to set timeouts via the <literal>TIMEOUT()</literal> function in this macro.</para>
</note>
<warning><para>Be aware of the limitations that macros have, specifically with regards to use of
the <literal>WaitExten</literal> application. For more information, see the documentation for
- Macro()</para></warning>
+ <literal>Macro()</literal>.</para></warning>
</option>
<option name="n">
<argument name="delete">
@@ -339,7 +341,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</option>
<option name="N">
<para>This option is a modifier for the call screening/privacy mode. It specifies
- that if Caller*ID is present, do not screen the call.</para>
+ that if CallerID is present, do not screen the call.</para>
</option>
<option name="o">
<argument name="x" required="false" />
@@ -347,7 +349,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<emphasis>calling</emphasis> channel be stored as the CallerID on the <emphasis>called</emphasis> channel.
This was the behavior of Asterisk 1.0 and earlier.
If <replaceable>x</replaceable> is provided, specify the CallerID stored on the <emphasis>called</emphasis> channel.
- Note that o(${CALLERID(all)}) is similar to option o without the parameter.</para>
+ Note that <literal>o(${CALLERID(all)})</literal> is similar to option <literal>o</literal> without the parameter.</para>
</option>
<option name="O">
<argument name="mode">
@@ -377,13 +379,13 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>Default: Indicate ringing to the calling party, even if the called party isn't actually ringing. Pass no audio to the calling
party until the called channel has answered.</para>
<argument name="tone" required="false">
- <para>Indicate progress to calling party. Send audio 'tone' from the indications.conf tonezone currently in use.</para>
+ <para>Indicate progress to calling party. Send audio 'tone' from the <filename>indications.conf</filename> tonezone currently in use.</para>
</argument>
</option>
- <option name="R">
- <para>Default: Indicate ringing to the calling party, even if the called party isn't actually ringing.
+ <option name="R">
+ <para>Default: Indicate ringing to the calling party, even if the called party isn't actually ringing.
Allow interruption of the ringback if early media is received on the channel.</para>
- </option>
+ </option>
<option name="S">
<argument name="x" required="true" />
<para>Hang up the call <replaceable>x</replaceable> seconds <emphasis>after</emphasis> the called party has
@@ -391,8 +393,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</option>
<option name="s">
<argument name="x" required="true" />
- <para>Force the outgoing callerid tag parameter to be set to the string <replaceable>x</replaceable>.</para>
- <para>Works with the f option.</para>
+ <para>Force the outgoing CallerID tag parameter to be set to the string <replaceable>x</replaceable>.</para>
+ <para>Works with the <literal>f</literal> option.</para>
</option>
<option name="t">
<para>Allow the called party to transfer the calling party by sending the
@@ -406,15 +408,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</option>
<option name="U" argsep="^">
<argument name="x" required="true">
- <para>Name of the subroutine to execute via Gosub</para>
+ <para>Name of the subroutine to execute via <literal>Gosub</literal></para>
</argument>
<argument name="arg" multiple="true" required="false">
- <para>Arguments for the Gosub routine</para>
+ <para>Arguments for the <literal>Gosub</literal> routine</para>
</argument>
- <para>Execute via Gosub the routine <replaceable>x</replaceable> for the <emphasis>called</emphasis> channel before connecting
- to the calling channel. Arguments can be specified to the Gosub
- using <literal>^</literal> as a delimiter. The Gosub routine can set the variable
- <variable>GOSUB_RESULT</variable> to specify the following actions after the Gosub returns.</para>
+ <para>Execute via <literal>Gosub</literal> the routine <replaceable>x</replaceable> for the <emphasis>called</emphasis> channel before connecting
+ to the calling channel. Arguments can be specified to the <literal>Gosub</literal>
+ using <literal>^</literal> as a delimiter. The <literal>Gosub</literal> routine can set the variable
+ <variable>GOSUB_RESULT</variable> to specify the following actions after the <literal>Gosub</literal> returns.</para>
<variablelist>
<variable name="GOSUB_RESULT">
<value name="ABORT">
@@ -438,7 +440,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<note>
<para>You cannot use any additional action post answer options in conjunction
with this option. Also, pbx services are run on the peer (called) channel,
- so you will not be able to set timeouts via the TIMEOUT() function in this routine.</para>
+ so you will not be able to set timeouts via the <literal>TIMEOUT()</literal> function in this routine.</para>
</note>
</option>
<option name="u">
@@ -455,7 +457,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<literal>prohib</literal>
<literal>unavailable</literal></para>
</argument>
- <para>Works with the f option.</para>
+ <para>Works with the <literal>f</literal> option.</para>
</option>
<option name="w">
<para>Allow the called party to enable recording of the call by sending
@@ -497,11 +499,59 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
hangs up, or if the call is bridged and either of the parties in the bridge
ends the call.</para>
<para>If the <variable>OUTBOUND_GROUP</variable> variable is set, all peer channels created by this
- application will be put into that group (as in Set(GROUP()=...).
+ application will be put into that group (as in <literal>Set(GROUP()=...</literal>).
If the <variable>OUTBOUND_GROUP_ONCE</variable> variable is set, all peer channels created by this
- application will be put into that group (as in Set(GROUP()=...). Unlike <variable>OUTBOUND_GROUP</variable>,
+ application will be put into that group (as in <literal>Set(GROUP()=...</literal>). Unlike <variable>OUTBOUND_GROUP</variable>,
however, the variable will be unset after use.</para>
+ <example title="Dial with 30 second timeout">
+ same => n,Dial(PJSIP/alice,30)
+ </example>
+ <example title="Parallel dial with 45 second timeout">
+ same => n,Dial(PJSIP/alice&amp;PJIP/bob,45)
+ </example>
+ <example title="Dial with 'g' continuation option">
+ same => n,Dial(PJSIP/alice,,g)
+ same => n,Log(NOTICE, Alice call result: ${DIALSTATUS})
+ </example>
+ <example title="Dial with transfer/recording features for calling party">
+ same => n,Dial(PJSIP/alice,,TX)
+ </example>
+ <example title="Dial with call length limit">
+ same => n,Dial(PJSIP/alice,,L(60000:30000:10000))
+ </example>
+ <example title="Dial with pre-dial subroutines">
+ [default]
+
+ exten => callee_channel,1,NoOp()
+ same => n,Log(NOTICE, I'm called on channel ${CHANNEL} prior to it starting the dial attempt)
+ same => n,Return()
+
+ exten => called_channel,1,NoOp()
+ same => n,Log(NOTICE, I'm called on outbound channel ${CHANNEL} prior to it being used to dial someone)
+ same => n,Return()
+
+ exten => _X.,1,NoOp()
+ same => n,Dial(PJSIP/alice,,b(default^called_channel^1)B(default^callee_channel^1))
+ same => n,Hangup()
+ </example>
+ <example title="Dial with post-answer subroutine executed on outbound channel">
+ [default]
+
+ exten => called_channel,1,NoOp()
+ same => n,Playback(hello)
+ same => n,Return()
+
+ exten => _X.,1,NoOp()
+ same => n,Dial(PJSIP/alice,,U(default^called_channel^1))
+ same => n,Hangup()
+ </example>
+ <example title="Dial into ConfBridge using 'G' option">
+ same => n,Dial(PJSIP/alice,,G(jump_to_here))
+ same => n(jump_to_here),Goto(confbridge)
+ same => n,Goto(confbridge)
+ same => n(confbridge),ConfBridge(${EXTEN})
+ </example>
<para>This application sets the following channel variables:</para>
<variablelist>
<variable name="DIALEDTIME">
@@ -510,6 +560,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<variable name="ANSWEREDTIME">
<para>This is the amount of time for actual call.</para>
</variable>
+ <variable name="DIALEDPEERNAME">
+ <para>The name of the outbound channel that answered the call.</para>
+ </variable>
+ <variable name="DIALEDPEERNUMBER">
+ <para>The number that was dialed for the answered outbound channel.</para>
+ </variable>
+ <variable name="FORWARDERNAME">
+ <para>If a call forward occurred, the name of the forwarded channel.</para>
+ </variable>
<variable name="DIALSTATUS">
<para>This is the status of the call</para>
<value name="CHANUNAVAIL" />
@@ -530,6 +589,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</variable>
</variablelist>
</description>
+ <see-also>
+ <ref type="application">RetryDial</ref>
+ <ref type="application">SendDTMF</ref>
+ <ref type="application">Gosub</ref>
+ <ref type="application">Macro</ref>
+ </see-also>
</application>
<application name="RetryDial" language="en_US">
<synopsis>
@@ -562,6 +627,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
The <replaceable>dialargs</replaceable> are specified in the same format that arguments are provided
to the Dial application.</para>
</description>
+ <see-also>
+ <ref type="application">Dial</ref>
+ </see-also>
</application>
***/
diff --git a/apps/app_queue.c b/apps/app_queue.c
index 3d22f9821..e04942f68 100644
--- a/apps/app_queue.c
+++ b/apps/app_queue.c
@@ -2337,10 +2337,17 @@ static void pending_members_remove(struct member *mem)
*/
static void update_status(struct call_queue *q, struct member *m, const int status)
{
- m->status = status;
-
- /* Whatever the status is clear the member from the pending members pool */
- pending_members_remove(m);
+ if (m->status != status) {
+ m->status = status;
+
+ /* Remove the member from the pending members pool only when the status changes.
+ * This is not done unconditionally because we can occasionally see multiple
+ * device state notifications of not in use after a previous call has ended,
+ * including after we have initiated a new call. This is more likely to
+ * happen when there is latency in the connection to the member.
+ */
+ pending_members_remove(m);
+ }
queue_publish_member_blob(queue_member_status_type(), queue_member_blob_create(q, m));
}
@@ -4831,6 +4838,7 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
char tmpchan[256];
char *stuff;
char *tech;
+ int failed = 0;
ast_copy_string(tmpchan, ast_channel_call_forward(o->chan), sizeof(tmpchan));
ast_copy_string(forwarder, ast_channel_name(o->chan), sizeof(forwarder));
@@ -4943,14 +4951,20 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
if (ast_call(o->chan, stuff, 0)) {
ast_log(LOG_NOTICE, "Forwarding failed to dial '%s/%s'\n",
tech, stuff);
- do_hang(o);
- numnochan++;
+ failed = 1;
}
}
- ast_channel_publish_dial(qe->chan, o->chan, stuff, NULL);
ast_channel_publish_dial_forward(qe->chan, original, o->chan, NULL,
"CANCEL", ast_channel_call_forward(original));
+ if (o->chan) {
+ ast_channel_publish_dial(qe->chan, o->chan, stuff, NULL);
+ }
+
+ if (failed) {
+ do_hang(o);
+ numnochan++;
+ }
/* Hangup the original channel now, in case we needed it */
ast_hangup(winner);
diff --git a/apps/app_userevent.c b/apps/app_userevent.c
index 8f7219eab..96a455cfb 100644
--- a/apps/app_userevent.c
+++ b/apps/app_userevent.c
@@ -62,6 +62,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<replaceable>eventname</replaceable> under the
<literal>eventname</literal> key.</para>
</description>
+ <see-also>
+ <ref type="manager">UserEvent</ref>
+ <ref type="managerEvent">UserEvent</ref>
+ </see-also>
</application>
***/
diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c
index a561729fb..5eb767c1c 100644
--- a/apps/app_voicemail.c
+++ b/apps/app_voicemail.c
@@ -3263,7 +3263,8 @@ void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
{
struct ast_str *str;
- if (!DEBUG_ATLEAST(5) || !(str = ast_str_create(MAX_OBJECT_FIELD))) {
+
+ if (!DEBUG_ATLEAST(5) || !(str = ast_str_create(256))) {
return;
}
@@ -12227,6 +12228,7 @@ static int append_mailbox(const char *context, const char *box, const char *data
return 0;
}
+#ifdef TEST_FRAMEWORK
AST_TEST_DEFINE(test_voicemail_vmuser)
{
int res = 0;
@@ -12414,6 +12416,7 @@ AST_TEST_DEFINE(test_voicemail_vmuser)
free_user(vmu);
return res ? AST_TEST_FAIL : AST_TEST_PASS;
}
+#endif
static int vm_box_exists(struct ast_channel *chan, const char *data)
{
@@ -13450,6 +13453,8 @@ static int actual_load_config(int reload, struct ast_config *cfg, struct ast_con
int x;
unsigned int tmpadsi[4];
char secretfn[PATH_MAX] = "";
+ long tps_queue_low;
+ long tps_queue_high;
#ifdef IMAP_STORAGE
ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
@@ -14025,6 +14030,25 @@ static int actual_load_config(int reload, struct ast_config *cfg, struct ast_con
pagerbody = ast_strdup(substitute_escapes(val));
}
+ tps_queue_high = AST_TASKPROCESSOR_HIGH_WATER_LEVEL;
+ if ((val = ast_variable_retrieve(cfg, "general", "tps_queue_high"))) {
+ if (sscanf(val, "%30ld", &tps_queue_high) != 1 || tps_queue_high <= 0) {
+ ast_log(AST_LOG_WARNING, "Invalid the taskprocessor high water alert trigger level '%s'\n", val);
+ tps_queue_high = AST_TASKPROCESSOR_HIGH_WATER_LEVEL;
+ }
+ }
+ tps_queue_low = -1;
+ if ((val = ast_variable_retrieve(cfg, "general", "tps_queue_low"))) {
+ if (sscanf(val, "%30ld", &tps_queue_low) != 1 ||
+ tps_queue_low < -1 || tps_queue_high < tps_queue_low) {
+ ast_log(AST_LOG_WARNING, "Invalid the taskprocessor low water clear alert level '%s'\n", val);
+ tps_queue_low = -1;
+ }
+ }
+ if (ast_taskprocessor_alert_set_levels(mwi_subscription_tps, tps_queue_low, tps_queue_high)) {
+ ast_log(AST_LOG_WARNING, "Failed to set alert levels for voicemail taskprocessor.\n");
+ }
+
/* load mailboxes from users.conf */
if (ucfg) {
for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
diff --git a/autoconf/acx_pthread.m4 b/autoconf/acx_pthread.m4
deleted file mode 100644
index b7ee73ddb..000000000
--- a/autoconf/acx_pthread.m4
+++ /dev/null
@@ -1,243 +0,0 @@
-dnl @synopsis ACX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
-dnl
-dnl @summary figure out how to build C programs using POSIX threads
-dnl
-dnl This macro figures out how to build C programs using POSIX threads.
-dnl It sets the PTHREAD_LIBS output variable to the threads library and
-dnl linker flags, and the PTHREAD_CFLAGS output variable to any special
-dnl C compiler flags that are needed. (The user can also force certain
-dnl compiler flags/libs to be tested by setting these environment
-dnl variables.)
-dnl
-dnl Also sets PTHREAD_CC to any special C compiler that is needed for
-dnl multi-threaded programs (defaults to the value of CC otherwise).
-dnl (This is necessary on AIX to use the special cc_r compiler alias.)
-dnl
-dnl NOTE: You are assumed to not only compile your program with these
-dnl flags, but also link it with them as well. e.g. you should link
-dnl with $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS
-dnl $LIBS
-dnl
-dnl If you are only building threads programs, you may wish to use
-dnl these variables in your default LIBS, CFLAGS, and CC:
-dnl
-dnl LIBS="$PTHREAD_LIBS $LIBS"
-dnl CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
-dnl CC="$PTHREAD_CC"
-dnl
-dnl In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute
-dnl constant has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to
-dnl that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
-dnl
-dnl ACTION-IF-FOUND is a list of shell commands to run if a threads
-dnl library is found, and ACTION-IF-NOT-FOUND is a list of commands to
-dnl run it if it is not found. If ACTION-IF-FOUND is not specified, the
-dnl default action will define HAVE_PTHREAD.
-dnl
-dnl Please let the authors know if this macro fails on any platform, or
-dnl if you have any other suggestions or comments. This macro was based
-dnl on work by SGJ on autoconf scripts for FFTW (www.fftw.org) (with
-dnl help from M. Frigo), as well as ac_pthread and hb_pthread macros
-dnl posted by Alejandro Forero Cuervo to the autoconf macro repository.
-dnl We are also grateful for the helpful feedback of numerous users.
-dnl
-dnl @category InstalledPackages
-dnl @author Steven G. Johnson <stevenj@alum.mit.edu>
-dnl @version 2006-05-29
-dnl @license GPLWithACException
-
-AC_DEFUN([ACX_PTHREAD],
-[
-AC_REQUIRE([AC_CANONICAL_HOST])
-AC_LANG_SAVE
-AC_LANG_C
-acx_pthread_ok=no
-
-# We used to check for pthread.h first, but this fails if pthread.h
-# requires special compiler flags (e.g. on True64 or Sequent).
-# It gets checked for in the link test anyway.
-
-# First of all, check if the user has set any of the PTHREAD_LIBS,
-# etcetera environment variables, and if threads linking works using
-# them:
-if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
- save_CFLAGS="$CFLAGS"
- CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
- save_LIBS="$LIBS"
- LIBS="$PTHREAD_LIBS $LIBS"
- AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
- AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes)
- AC_MSG_RESULT($acx_pthread_ok)
- if test x"$acx_pthread_ok" = xno; then
- PTHREAD_LIBS=""
- PTHREAD_CFLAGS=""
- fi
- LIBS="$save_LIBS"
- CFLAGS="$save_CFLAGS"
-fi
-
-# We must check for the threads library under a number of different
-# names; the ordering is very important because some systems
-# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
-# libraries is broken (non-POSIX).
-
-# Create a list of thread flags to try. Items starting with a "-" are
-# C compiler flags, and other items are library names, except for "none"
-# which indicates that we try without any flags at all, and "pthread-config"
-# which is a program returning the flags for the Pth emulation library.
-
-acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
-
-# The ordering *is* (sometimes) important. Some notes on the
-# individual items follow:
-
-# pthreads: AIX (must check this before -lpthread)
-# none: in case threads are in libc; should be tried before -Kthread and
-# other compiler flags to prevent continual compiler warnings
-# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
-# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
-# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
-# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
-# -pthreads: Solaris/gcc
-# -mthreads: Mingw32/gcc, Lynx/gcc
-# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
-# doesn't hurt to check since this sometimes defines pthreads too;
-# also defines -D_REENTRANT)
-# ... -mt is also the pthreads flag for HP/aCC
-# pthread: Linux, etcetera
-# --thread-safe: KAI C++
-# pthread-config: use pthread-config program (for GNU Pth library)
-
-case "${host_cpu}-${host_os}" in
- *solaris*)
-
- # On Solaris (at least, for some versions), libc contains stubbed
- # (non-functional) versions of the pthreads routines, so link-based
- # tests will erroneously succeed. (We need to link with -pthreads/-mt/
- # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
- # a function called by this macro, so we could check for that, but
- # who knows whether they'll stub that too in a future libc.) So,
- # we'll just look for -pthreads and -lpthread first:
-
- acx_pthread_flags="-pthreads pthread -mt -pthread $acx_pthread_flags"
- ;;
-esac
-
-if test x"$acx_pthread_ok" = xno; then
-for flag in $acx_pthread_flags; do
-
- case $flag in
- none)
- AC_MSG_CHECKING([whether pthreads work without any flags])
- ;;
-
- -*)
- AC_MSG_CHECKING([whether pthreads work with $flag])
- PTHREAD_CFLAGS="$flag"
- ;;
-
- pthread-config)
- AC_CHECK_PROG(acx_pthread_config, pthread-config, yes, no)
- if test x"$acx_pthread_config" = xno; then continue; fi
- PTHREAD_CFLAGS="`pthread-config --cflags`"
- PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
- ;;
-
- *)
- AC_MSG_CHECKING([for the pthreads library -l$flag])
- PTHREAD_LIBS="-l$flag"
- ;;
- esac
-
- save_LIBS="$LIBS"
- save_CFLAGS="$CFLAGS"
- LIBS="$PTHREAD_LIBS $LIBS"
- CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
-
- # Check for various functions. We must include pthread.h,
- # since some functions may be macros. (On the Sequent, we
- # need a special flag -Kthread to make this header compile.)
- # We check for pthread_join because it is in -lpthread on IRIX
- # while pthread_create is in libc. We check for pthread_attr_init
- # due to DEC craziness with -lpthreads. We check for
- # pthread_cleanup_push because it is one of the few pthread
- # functions on Solaris that doesn't have a non-functional libc stub.
- # We try pthread_create on general principles.
- AC_TRY_LINK([#include <pthread.h>],
- [pthread_t th; pthread_join(th, 0);
- pthread_attr_init(0); pthread_cleanup_push(0, 0);
- pthread_create(0,0,0,0); pthread_cleanup_pop(0); ],
- [acx_pthread_ok=yes])
-
- LIBS="$save_LIBS"
- CFLAGS="$save_CFLAGS"
-
- AC_MSG_RESULT($acx_pthread_ok)
- if test "x$acx_pthread_ok" = xyes; then
- break;
- fi
-
- PTHREAD_LIBS=""
- PTHREAD_CFLAGS=""
-done
-fi
-
-# Various other checks:
-if test "x$acx_pthread_ok" = xyes; then
- save_LIBS="$LIBS"
- LIBS="$PTHREAD_LIBS $LIBS"
- save_CFLAGS="$CFLAGS"
- CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
-
- # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
- AC_MSG_CHECKING([for joinable pthread attribute])
- attr_name=unknown
- for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
- AC_TRY_LINK([#include <pthread.h>], [int attr=$attr; return attr;],
- [attr_name=$attr; break])
- done
- AC_MSG_RESULT($attr_name)
- if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
- AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name,
- [Define to necessary symbol if this constant
- uses a non-standard name on your system.])
- fi
-
- AC_MSG_CHECKING([if more special flags are required for pthreads])
- flag=no
- case "${host_cpu}-${host_os}" in
- *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";;
- *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";;
- esac
- AC_MSG_RESULT(${flag})
- if test "x$flag" != xno; then
- PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
- fi
-
- LIBS="$save_LIBS"
- CFLAGS="$save_CFLAGS"
-
- # More AIX lossage: must compile with xlc_r or cc_r
- if test x"$GCC" != xyes; then
- AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC})
- else
- PTHREAD_CC=$CC
- fi
-else
- PTHREAD_CC="$CC"
-fi
-
-AC_SUBST(PTHREAD_LIBS)
-AC_SUBST(PTHREAD_CFLAGS)
-AC_SUBST(PTHREAD_CC)
-
-# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
-if test x"$acx_pthread_ok" = xyes; then
- ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
- :
-else
- acx_pthread_ok=no
- $2
-fi
-AC_LANG_RESTORE
-])dnl ACX_PTHREAD
diff --git a/autoconf/ast_ext_lib.m4 b/autoconf/ast_ext_lib.m4
index 8f35f4bad..2c73b40c5 100644
--- a/autoconf/ast_ext_lib.m4
+++ b/autoconf/ast_ext_lib.m4
@@ -11,7 +11,7 @@ AC_DEFUN([AST_EXT_LIB_SETUP],
$1_DESCRIP="$2"
$1_OPTION="$3"
PBX_$1=0
- AC_ARG_WITH([$3], AC_HELP_STRING([--with-$3=PATH],[use $2 files in PATH$4]),
+ AC_ARG_WITH([$3], AS_HELP_STRING([--with-$3=PATH],[use $2 files in PATH$4]),
[
case ${withval} in
n|no)
diff --git a/autoconf/ast_prog_ld.m4 b/autoconf/ast_prog_ld.m4
index 9177fedb3..b69c2c2ac 100644
--- a/autoconf/ast_prog_ld.m4
+++ b/autoconf/ast_prog_ld.m4
@@ -3,7 +3,7 @@
# find the pathname to the GNU or non-GNU linker
AC_DEFUN([AST_PROG_LD],
[AC_ARG_WITH([gnu-ld],
- [AC_HELP_STRING([--with-gnu-ld],
+ [AS_HELP_STRING([--with-gnu-ld],
[assume the C compiler uses GNU ld @<:@default=no@:>@])],
[test "$withval" = no || with_gnu_ld=yes],
[with_gnu_ld=no])
diff --git a/autoconf/ax_pthread.m4 b/autoconf/ax_pthread.m4
new file mode 100644
index 000000000..4c4051ea3
--- /dev/null
+++ b/autoconf/ax_pthread.m4
@@ -0,0 +1,485 @@
+# ===========================================================================
+# http://www.gnu.org/software/autoconf-archive/ax_pthread.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
+#
+# DESCRIPTION
+#
+# This macro figures out how to build C programs using POSIX threads. It
+# sets the PTHREAD_LIBS output variable to the threads library and linker
+# flags, and the PTHREAD_CFLAGS output variable to any special C compiler
+# flags that are needed. (The user can also force certain compiler
+# flags/libs to be tested by setting these environment variables.)
+#
+# Also sets PTHREAD_CC to any special C compiler that is needed for
+# multi-threaded programs (defaults to the value of CC otherwise). (This
+# is necessary on AIX to use the special cc_r compiler alias.)
+#
+# NOTE: You are assumed to not only compile your program with these flags,
+# but also to link with them as well. For example, you might link with
+# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
+#
+# If you are only building threaded programs, you may wish to use these
+# variables in your default LIBS, CFLAGS, and CC:
+#
+# LIBS="$PTHREAD_LIBS $LIBS"
+# CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+# CC="$PTHREAD_CC"
+#
+# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
+# has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to
+# that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
+#
+# Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the
+# PTHREAD_PRIO_INHERIT symbol is defined when compiling with
+# PTHREAD_CFLAGS.
+#
+# ACTION-IF-FOUND is a list of shell commands to run if a threads library
+# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it
+# is not found. If ACTION-IF-FOUND is not specified, the default action
+# will define HAVE_PTHREAD.
+#
+# Please let the authors know if this macro fails on any platform, or if
+# you have any other suggestions or comments. This macro was based on work
+# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help
+# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by
+# Alejandro Forero Cuervo to the autoconf macro repository. We are also
+# grateful for the helpful feedback of numerous users.
+#
+# Updated for Autoconf 2.68 by Daniel Richard G.
+#
+# LICENSE
+#
+# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
+# Copyright (c) 2011 Daniel Richard G. <skunk@iSKUNK.ORG>
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+#
+# As a special exception, the respective Autoconf Macro's copyright owner
+# gives unlimited permission to copy, distribute and modify the configure
+# scripts that are the output of Autoconf when processing the Macro. You
+# need not follow the terms of the GNU General Public License when using
+# or distributing such scripts, even though portions of the text of the
+# Macro appear in them. The GNU General Public License (GPL) does govern
+# all other use of the material that constitutes the Autoconf Macro.
+#
+# This special exception to the GPL applies to versions of the Autoconf
+# Macro released by the Autoconf Archive. When you make and distribute a
+# modified version of the Autoconf Macro, you may extend this special
+# exception to the GPL to apply to your modified version as well.
+
+#serial 23
+
+AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
+AC_DEFUN([AX_PTHREAD], [
+AC_REQUIRE([AC_CANONICAL_HOST])
+AC_REQUIRE([AC_PROG_CC])
+AC_REQUIRE([AC_PROG_SED])
+AC_LANG_PUSH([C])
+ax_pthread_ok=no
+
+# We used to check for pthread.h first, but this fails if pthread.h
+# requires special compiler flags (e.g. on Tru64 or Sequent).
+# It gets checked for in the link test anyway.
+
+# First of all, check if the user has set any of the PTHREAD_LIBS,
+# etcetera environment variables, and if threads linking works using
+# them:
+if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then
+ ax_pthread_save_CC="$CC"
+ ax_pthread_save_CFLAGS="$CFLAGS"
+ ax_pthread_save_LIBS="$LIBS"
+ AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"])
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS])
+ AC_LINK_IFELSE([AC_LANG_CALL([], [pthread_join])], [ax_pthread_ok=yes])
+ AC_MSG_RESULT([$ax_pthread_ok])
+ if test "x$ax_pthread_ok" = "xno"; then
+ PTHREAD_LIBS=""
+ PTHREAD_CFLAGS=""
+ fi
+ CC="$ax_pthread_save_CC"
+ CFLAGS="$ax_pthread_save_CFLAGS"
+ LIBS="$ax_pthread_save_LIBS"
+fi
+
+# We must check for the threads library under a number of different
+# names; the ordering is very important because some systems
+# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
+# libraries is broken (non-POSIX).
+
+# Create a list of thread flags to try. Items starting with a "-" are
+# C compiler flags, and other items are library names, except for "none"
+# which indicates that we try without any flags at all, and "pthread-config"
+# which is a program returning the flags for the Pth emulation library.
+
+ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
+
+# The ordering *is* (sometimes) important. Some notes on the
+# individual items follow:
+
+# pthreads: AIX (must check this before -lpthread)
+# none: in case threads are in libc; should be tried before -Kthread and
+# other compiler flags to prevent continual compiler warnings
+# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
+# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64
+# (Note: HP C rejects this with "bad form for `-t' option")
+# -pthreads: Solaris/gcc (Note: HP C also rejects)
+# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
+# doesn't hurt to check since this sometimes defines pthreads and
+# -D_REENTRANT too), HP C (must be checked before -lpthread, which
+# is present but should not be used directly; and before -mthreads,
+# because the compiler interprets this as "-mt" + "-hreads")
+# -mthreads: Mingw32/gcc, Lynx/gcc
+# pthread: Linux, etcetera
+# --thread-safe: KAI C++
+# pthread-config: use pthread-config program (for GNU Pth library)
+
+case $host_os in
+
+ freebsd*)
+
+ # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
+ # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
+
+ ax_pthread_flags="-kthread lthread $ax_pthread_flags"
+ ;;
+
+ hpux*)
+
+ # From the cc(1) man page: "[-mt] Sets various -D flags to enable
+ # multi-threading and also sets -lpthread."
+
+ ax_pthread_flags="-mt -pthread pthread $ax_pthread_flags"
+ ;;
+
+ openedition*)
+
+ # IBM z/OS requires a feature-test macro to be defined in order to
+ # enable POSIX threads at all, so give the user a hint if this is
+ # not set. (We don't define these ourselves, as they can affect
+ # other portions of the system API in unpredictable ways.)
+
+ AC_EGREP_CPP([AX_PTHREAD_ZOS_MISSING],
+ [
+# if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS)
+ AX_PTHREAD_ZOS_MISSING
+# endif
+ ],
+ [AC_MSG_WARN([IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support.])])
+ ;;
+
+ solaris*)
+
+ # On Solaris (at least, for some versions), libc contains stubbed
+ # (non-functional) versions of the pthreads routines, so link-based
+ # tests will erroneously succeed. (N.B.: The stubs are missing
+ # pthread_cleanup_push, or rather a function called by this macro,
+ # so we could check for that, but who knows whether they'll stub
+ # that too in a future libc.) So we'll check first for the
+ # standard Solaris way of linking pthreads (-mt -lpthread).
+
+ ax_pthread_flags="-mt,pthread pthread $ax_pthread_flags"
+ ;;
+esac
+
+# GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC)
+
+AS_IF([test "x$GCC" = "xyes"],
+ [ax_pthread_flags="-pthread -pthreads $ax_pthread_flags"])
+
+# The presence of a feature test macro requesting re-entrant function
+# definitions is, on some systems, a strong hint that pthreads support is
+# correctly enabled
+
+case $host_os in
+ darwin* | hpux* | linux* | osf* | solaris*)
+ ax_pthread_check_macro="_REENTRANT"
+ ;;
+
+ aix*)
+ ax_pthread_check_macro="_THREAD_SAFE"
+ ;;
+
+ *)
+ ax_pthread_check_macro="--"
+ ;;
+esac
+AS_IF([test "x$ax_pthread_check_macro" = "x--"],
+ [ax_pthread_check_cond=0],
+ [ax_pthread_check_cond="!defined($ax_pthread_check_macro)"])
+
+# Are we compiling with Clang?
+
+AC_CACHE_CHECK([whether $CC is Clang],
+ [ax_cv_PTHREAD_CLANG],
+ [ax_cv_PTHREAD_CLANG=no
+ # Note that Autoconf sets GCC=yes for Clang as well as GCC
+ if test "x$GCC" = "xyes"; then
+ AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG],
+ [/* Note: Clang 2.7 lacks __clang_[a-z]+__ */
+# if defined(__clang__) && defined(__llvm__)
+ AX_PTHREAD_CC_IS_CLANG
+# endif
+ ],
+ [ax_cv_PTHREAD_CLANG=yes])
+ fi
+ ])
+ax_pthread_clang="$ax_cv_PTHREAD_CLANG"
+
+ax_pthread_clang_warning=no
+
+# Clang needs special handling, because older versions handle the -pthread
+# option in a rather... idiosyncratic way
+
+if test "x$ax_pthread_clang" = "xyes"; then
+
+ # Clang takes -pthread; it has never supported any other flag
+
+ # (Note 1: This will need to be revisited if a system that Clang
+ # supports has POSIX threads in a separate library. This tends not
+ # to be the way of modern systems, but it's conceivable.)
+
+ # (Note 2: On some systems, notably Darwin, -pthread is not needed
+ # to get POSIX threads support; the API is always present and
+ # active. We could reasonably leave PTHREAD_CFLAGS empty. But
+ # -pthread does define _REENTRANT, and while the Darwin headers
+ # ignore this macro, third-party headers might not.)
+
+ PTHREAD_CFLAGS="-pthread"
+ PTHREAD_LIBS=
+
+ ax_pthread_ok=yes
+
+ # However, older versions of Clang make a point of warning the user
+ # that, in an invocation where only linking and no compilation is
+ # taking place, the -pthread option has no effect ("argument unused
+ # during compilation"). They expect -pthread to be passed in only
+ # when source code is being compiled.
+ #
+ # Problem is, this is at odds with the way Automake and most other
+ # C build frameworks function, which is that the same flags used in
+ # compilation (CFLAGS) are also used in linking. Many systems
+ # supported by AX_PTHREAD require exactly this for POSIX threads
+ # support, and in fact it is often not straightforward to specify a
+ # flag that is used only in the compilation phase and not in
+ # linking. Such a scenario is extremely rare in practice.
+ #
+ # Even though use of the -pthread flag in linking would only print
+ # a warning, this can be a nuisance for well-run software projects
+ # that build with -Werror. So if the active version of Clang has
+ # this misfeature, we search for an option to squash it.
+
+ AC_CACHE_CHECK([whether Clang needs flag to prevent "argument unused" warning when linking with -pthread],
+ [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG],
+ [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown
+ # Create an alternate version of $ac_link that compiles and
+ # links in two steps (.c -> .o, .o -> exe) instead of one
+ # (.c -> exe), because the warning occurs only in the second
+ # step
+ ax_pthread_save_ac_link="$ac_link"
+ ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g'
+ ax_pthread_link_step=`$as_echo "$ac_link" | sed "$ax_pthread_sed"`
+ ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)"
+ ax_pthread_save_CFLAGS="$CFLAGS"
+ for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do
+ AS_IF([test "x$ax_pthread_try" = "xunknown"], [break])
+ CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS"
+ ac_link="$ax_pthread_save_ac_link"
+ AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])],
+ [ac_link="$ax_pthread_2step_ac_link"
+ AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])],
+ [break])
+ ])
+ done
+ ac_link="$ax_pthread_save_ac_link"
+ CFLAGS="$ax_pthread_save_CFLAGS"
+ AS_IF([test "x$ax_pthread_try" = "x"], [ax_pthread_try=no])
+ ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try"
+ ])
+
+ case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in
+ no | unknown) ;;
+ *) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;;
+ esac
+
+fi # $ax_pthread_clang = yes
+
+if test "x$ax_pthread_ok" = "xno"; then
+for ax_pthread_try_flag in $ax_pthread_flags; do
+
+ case $ax_pthread_try_flag in
+ none)
+ AC_MSG_CHECKING([whether pthreads work without any flags])
+ ;;
+
+ -mt,pthread)
+ AC_MSG_CHECKING([whether pthreads work with -mt -lpthread])
+ PTHREAD_CFLAGS="-mt"
+ PTHREAD_LIBS="-lpthread"
+ ;;
+
+ -*)
+ AC_MSG_CHECKING([whether pthreads work with $ax_pthread_try_flag])
+ PTHREAD_CFLAGS="$ax_pthread_try_flag"
+ ;;
+
+ pthread-config)
+ AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no])
+ AS_IF([test "x$ax_pthread_config" = "xno"], [continue])
+ PTHREAD_CFLAGS="`pthread-config --cflags`"
+ PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
+ ;;
+
+ *)
+ AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag])
+ PTHREAD_LIBS="-l$ax_pthread_try_flag"
+ ;;
+ esac
+
+ ax_pthread_save_CFLAGS="$CFLAGS"
+ ax_pthread_save_LIBS="$LIBS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+
+ # Check for various functions. We must include pthread.h,
+ # since some functions may be macros. (On the Sequent, we
+ # need a special flag -Kthread to make this header compile.)
+ # We check for pthread_join because it is in -lpthread on IRIX
+ # while pthread_create is in libc. We check for pthread_attr_init
+ # due to DEC craziness with -lpthreads. We check for
+ # pthread_cleanup_push because it is one of the few pthread
+ # functions on Solaris that doesn't have a non-functional libc stub.
+ # We try pthread_create on general principles.
+
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>
+# if $ax_pthread_check_cond
+# error "$ax_pthread_check_macro must be defined"
+# endif
+ static void routine(void *a) { a = 0; }
+ static void *start_routine(void *a) { return a; }],
+ [pthread_t th; pthread_attr_t attr;
+ pthread_create(&th, 0, start_routine, 0);
+ pthread_join(th, 0);
+ pthread_attr_init(&attr);
+ pthread_cleanup_push(routine, 0);
+ pthread_cleanup_pop(0) /* ; */])],
+ [ax_pthread_ok=yes],
+ [])
+
+ CFLAGS="$ax_pthread_save_CFLAGS"
+ LIBS="$ax_pthread_save_LIBS"
+
+ AC_MSG_RESULT([$ax_pthread_ok])
+ AS_IF([test "x$ax_pthread_ok" = "xyes"], [break])
+
+ PTHREAD_LIBS=""
+ PTHREAD_CFLAGS=""
+done
+fi
+
+# Various other checks:
+if test "x$ax_pthread_ok" = "xyes"; then
+ ax_pthread_save_CFLAGS="$CFLAGS"
+ ax_pthread_save_LIBS="$LIBS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+
+ # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
+ AC_CACHE_CHECK([for joinable pthread attribute],
+ [ax_cv_PTHREAD_JOINABLE_ATTR],
+ [ax_cv_PTHREAD_JOINABLE_ATTR=unknown
+ for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>],
+ [int attr = $ax_pthread_attr; return attr /* ; */])],
+ [ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break],
+ [])
+ done
+ ])
+ AS_IF([test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \
+ test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \
+ test "x$ax_pthread_joinable_attr_defined" != "xyes"],
+ [AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE],
+ [$ax_cv_PTHREAD_JOINABLE_ATTR],
+ [Define to necessary symbol if this constant
+ uses a non-standard name on your system.])
+ ax_pthread_joinable_attr_defined=yes
+ ])
+
+ AC_CACHE_CHECK([whether more special flags are required for pthreads],
+ [ax_cv_PTHREAD_SPECIAL_FLAGS],
+ [ax_cv_PTHREAD_SPECIAL_FLAGS=no
+ case $host_os in
+ solaris*)
+ ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS"
+ ;;
+ esac
+ ])
+ AS_IF([test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \
+ test "x$ax_pthread_special_flags_added" != "xyes"],
+ [PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS"
+ ax_pthread_special_flags_added=yes])
+
+ AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT],
+ [ax_cv_PTHREAD_PRIO_INHERIT],
+ [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread.h>]],
+ [[int i = PTHREAD_PRIO_INHERIT;]])],
+ [ax_cv_PTHREAD_PRIO_INHERIT=yes],
+ [ax_cv_PTHREAD_PRIO_INHERIT=no])
+ ])
+ AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \
+ test "x$ax_pthread_prio_inherit_defined" != "xyes"],
+ [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.])
+ ax_pthread_prio_inherit_defined=yes
+ ])
+
+ CFLAGS="$ax_pthread_save_CFLAGS"
+ LIBS="$ax_pthread_save_LIBS"
+
+ # More AIX lossage: compile with *_r variant
+ if test "x$GCC" != "xyes"; then
+ case $host_os in
+ aix*)
+ AS_CASE(["x/$CC"],
+ [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6],
+ [#handle absolute path differently from PATH based program lookup
+ AS_CASE(["x$CC"],
+ [x/*],
+ [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])],
+ [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])])
+ ;;
+ esac
+ fi
+fi
+
+test -n "$PTHREAD_CC" || PTHREAD_CC="$CC"
+
+AC_SUBST([PTHREAD_LIBS])
+AC_SUBST([PTHREAD_CFLAGS])
+AC_SUBST([PTHREAD_CC])
+
+# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
+if test "x$ax_pthread_ok" = "xyes"; then
+ ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1])
+ :
+else
+ ax_pthread_ok=no
+ $2
+fi
+AC_LANG_POP
+])dnl AX_PTHREAD
diff --git a/autoconf/libcurl.m4 b/autoconf/libcurl.m4
index 085ee0ea0..9a31bfc8b 100644
--- a/autoconf/libcurl.m4
+++ b/autoconf/libcurl.m4
@@ -1,3 +1,24 @@
+#***************************************************************************
+# _ _ ____ _
+# Project ___| | | | _ \| |
+# / __| | | | |_) | |
+# | (__| |_| | _ <| |___
+# \___|\___/|_| \_\_____|
+#
+# Copyright (C) 2006, David Shaw <dshaw@jabberwocky.com>
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at https://curl.haxx.se/docs/copyright.html.
+#
+# You may opt to use, copy, modify, merge, publish, distribute and/or sell
+# copies of the Software, and permit persons to whom the Software is
+# furnished to do so, under the terms of the COPYING file.
+#
+# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+# KIND, either express or implied.
+#
+###########################################################################
# LIBCURL_CHECK_CONFIG ([DEFAULT-ACTION], [MINIMUM-VERSION],
# [ACTION-IF-YES], [ACTION-IF-NO])
# ----------------------------------------------------------
@@ -55,10 +76,14 @@ AC_DEFUN([AST_LIBCURL_CHECK_CONFIG],
AH_TEMPLATE([LIBCURL_PROTOCOL_LDAP],[Defined if libcurl supports LDAP])
AH_TEMPLATE([LIBCURL_PROTOCOL_DICT],[Defined if libcurl supports DICT])
AH_TEMPLATE([LIBCURL_PROTOCOL_TFTP],[Defined if libcurl supports TFTP])
+ AH_TEMPLATE([LIBCURL_PROTOCOL_RTSP],[Defined if libcurl supports RTSP])
+ AH_TEMPLATE([LIBCURL_PROTOCOL_POP3],[Defined if libcurl supports POP3])
+ AH_TEMPLATE([LIBCURL_PROTOCOL_IMAP],[Defined if libcurl supports IMAP])
+ AH_TEMPLATE([LIBCURL_PROTOCOL_SMTP],[Defined if libcurl supports SMTP])
AC_SUBST(PBX_CURL)
AC_ARG_WITH(libcurl,
- AC_HELP_STRING([--with-libcurl=DIR],[look for the curl library in DIR]),
+ AS_HELP_STRING([--with-libcurl=PREFIX],[look for the curl library in PREFIX/lib and headers in PREFIX/include]),
[_libcurl_with=$withval],[_libcurl_with=ifelse([$1],,[yes],[$1])])
if test "$_libcurl_with" != "no" ; then
@@ -72,10 +97,10 @@ AC_DEFUN([AST_LIBCURL_CHECK_CONFIG],
if test -d "$_libcurl_with" ; then
CURL_INCLUDE="-I$withval/include"
_libcurl_ldflags="-L$withval/lib"
- AC_PATH_PROG([_libcurl_config],[curl-config],["$withval/bin"],
+ AC_PATH_PROG([_libcurl_config],[curl-config],[],
["$withval/bin"])
else
- AC_PATH_PROG([_libcurl_config],[curl-config])
+ AC_PATH_PROG([_libcurl_config],[curl-config],[],[$PATH])
fi
if test x$_libcurl_config != "x" ; then
@@ -143,18 +168,19 @@ AC_DEFUN([AST_LIBCURL_CHECK_CONFIG],
_libcurl_save_libs=$LIBS
LIBS="$CURL_LIB $LIBS"
- AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <curl/curl.h>],[
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <curl/curl.h>]],[[
/* Try and use a few common options to force a failure if we are
missing symbols or can't link. */
int x;
curl_easy_setopt(NULL,CURLOPT_URL,NULL);
x=CURL_ERROR_SIZE;
x=CURLOPT_WRITEFUNCTION;
-x=CURLOPT_FILE;
+x=CURLOPT_WRITEDATA;
x=CURLOPT_ERRORBUFFER;
x=CURLOPT_STDERR;
x=CURLOPT_VERBOSE;
-])],libcurl_cv_lib_curl_usable=yes,libcurl_cv_lib_curl_usable=no)
+if (x) {;}
+]])],libcurl_cv_lib_curl_usable=yes,libcurl_cv_lib_curl_usable=no)
CPPFLAGS=$_libcurl_save_cppflags
LIBS=$_libcurl_save_libs
@@ -196,17 +222,23 @@ x=CURLOPT_VERBOSE;
# We don't have --protocols, so just assume that all
# protocols are available
- _libcurl_protocols="HTTP FTP FILE TELNET LDAP DICT"
+ _libcurl_protocols="HTTP FTP FILE TELNET LDAP DICT TFTP"
if test x$libcurl_feature_SSL = xyes ; then
_libcurl_protocols="$_libcurl_protocols HTTPS"
# FTPS wasn't standards-compliant until version
- # 7.11.0
+ # 7.11.0 (0x070b00 == 461568)
if test $_libcurl_version -ge 461568; then
_libcurl_protocols="$_libcurl_protocols FTPS"
fi
fi
+
+ # RTSP, IMAP, POP3 and SMTP were added in
+ # 7.20.0 (0x071400 == 463872)
+ if test $_libcurl_version -ge 463872; then
+ _libcurl_protocols="$_libcurl_protocols RTSP IMAP POP3 SMTP"
+ fi
fi
for _libcurl_protocol in $_libcurl_protocols ; do
@@ -241,4 +273,3 @@ x=CURLOPT_VERBOSE;
unset _libcurl_with
])dnl
-
diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c
index e9d0b5bd3..ac55f5864 100644
--- a/channels/chan_dahdi.c
+++ b/channels/chan_dahdi.c
@@ -180,6 +180,60 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>This application will Accept the R2 call either with charge or no charge.</para>
</description>
</application>
+ <info name="DAHDICHANNEL" language="en_US" tech="DAHDI">
+ <enumlist>
+ <enum name="dahdi_channel">
+ <para>R/O DAHDI channel related to this channel.</para>
+ </enum>
+ <enum name="dahdi_span">
+ <para>R/O DAHDI span related to this channel.</para>
+ </enum>
+ <enum name="dahdi_type">
+ <para>R/O DAHDI channel type, one of:</para>
+ <enumlist>
+ <enum name="analog" />
+ <enum name="mfc/r2" />
+ <enum name="pri" />
+ <enum name="pseudo" />
+ <enum name="ss7" />
+ </enumlist>
+ </enum>
+ <enum name="keypad_digits">
+ <para>R/O PRI Keypad digits that came in with the SETUP message.</para>
+ </enum>
+ <enum name="reversecharge">
+ <para>R/O PRI Reverse Charging Indication, one of:</para>
+ <enumlist>
+ <enum name="-1"> <para>None</para></enum>
+ <enum name=" 1"> <para>Reverse Charging Requested</para></enum>
+ </enumlist>
+ </enum>
+ <enum name="no_media_path">
+ <para>R/O PRI Nonzero if the channel has no B channel.
+ The channel is either on hold or a call waiting call.</para>
+ </enum>
+ <enum name="buffers">
+ <para>W/O Change the channel's buffer policy (for the current call only)</para>
+ <para>This option takes two arguments:</para>
+ <para> Number of buffers,</para>
+ <para> Buffer policy being one of:</para>
+ <para> <literal>full</literal></para>
+ <para> <literal>immediate</literal></para>
+ <para> <literal>half</literal></para>
+ </enum>
+ <enum name="echocan_mode">
+ <para>W/O Change the configuration of the active echo
+ canceller on the channel (if any), for the current call
+ only.</para>
+ <para>Possible values are:</para>
+ <para> <literal>on</literal> Normal mode (the echo canceller is actually reinitalized)</para>
+ <para> <literal>off</literal> Disabled</para>
+ <para> <literal>fax</literal> FAX/data mode (NLP disabled if possible, otherwise
+ completely disabled)</para>
+ <para> <literal>voice</literal> Voice mode (returns from FAX mode, reverting the changes that were made)</para>
+ </enum>
+ </enumlist>
+ </info>
<manager name="DAHDITransfer" language="en_US">
<synopsis>
Transfer DAHDI Channel.
@@ -1696,26 +1750,28 @@ static void my_handle_dtmf(void *pvt, struct ast_channel *ast, enum analog_sub a
if (strcmp(ast_channel_exten(ast), "fax")) {
const char *target_context = S_OR(ast_channel_macrocontext(ast), ast_channel_context(ast));
- /* We need to unlock 'ast' here because ast_exists_extension has the
+ /*
+ * We need to unlock 'ast' here because ast_exists_extension has the
* potential to start autoservice on the channel. Such action is prone
- * to deadlock.
+ * to deadlock if the channel is locked.
+ *
+ * ast_async_goto() has its own restriction on not holding the
+ * channel lock.
*/
ast_mutex_unlock(&p->lock);
ast_channel_unlock(ast);
if (ast_exists_extension(ast, target_context, "fax", 1,
S_COR(ast_channel_caller(ast)->id.number.valid, ast_channel_caller(ast)->id.number.str, NULL))) {
- ast_channel_lock(ast);
- ast_mutex_lock(&p->lock);
ast_verb(3, "Redirecting %s to fax extension\n", ast_channel_name(ast));
/* Save the DID/DNIS when we transfer the fax call to a "fax" extension */
pbx_builtin_setvar_helper(ast, "FAXEXTEN", ast_channel_exten(ast));
if (ast_async_goto(ast, target_context, "fax", 1))
ast_log(LOG_WARNING, "Failed to async goto '%s' into fax of '%s'\n", ast_channel_name(ast), target_context);
} else {
- ast_channel_lock(ast);
- ast_mutex_lock(&p->lock);
ast_log(LOG_NOTICE, "Fax detected, but no fax extension\n");
}
+ ast_channel_lock(ast);
+ ast_mutex_lock(&p->lock);
} else {
ast_debug(1, "Already in a fax extension, not redirecting\n");
}
@@ -2348,7 +2404,6 @@ static void my_pri_ss7_open_media(void *p)
if (pvt->dsp_features && pvt->dsp) {
ast_dsp_set_features(pvt->dsp, pvt->dsp_features);
- pvt->dsp_features = 0;
}
}
#endif /* defined(HAVE_PRI) || defined(HAVE_SS7) */
@@ -7203,26 +7258,28 @@ static void dahdi_handle_dtmf(struct ast_channel *ast, int idx, struct ast_frame
if (strcmp(ast_channel_exten(ast), "fax")) {
const char *target_context = S_OR(ast_channel_macrocontext(ast), ast_channel_context(ast));
- /* We need to unlock 'ast' here because ast_exists_extension has the
+ /*
+ * We need to unlock 'ast' here because ast_exists_extension has the
* potential to start autoservice on the channel. Such action is prone
- * to deadlock.
+ * to deadlock if the channel is locked.
+ *
+ * ast_async_goto() has its own restriction on not holding the
+ * channel lock.
*/
ast_mutex_unlock(&p->lock);
ast_channel_unlock(ast);
if (ast_exists_extension(ast, target_context, "fax", 1,
S_COR(ast_channel_caller(ast)->id.number.valid, ast_channel_caller(ast)->id.number.str, NULL))) {
- ast_channel_lock(ast);
- ast_mutex_lock(&p->lock);
ast_verb(3, "Redirecting %s to fax extension\n", ast_channel_name(ast));
/* Save the DID/DNIS when we transfer the fax call to a "fax" extension */
pbx_builtin_setvar_helper(ast, "FAXEXTEN", ast_channel_exten(ast));
if (ast_async_goto(ast, target_context, "fax", 1))
ast_log(LOG_WARNING, "Failed to async goto '%s' into fax of '%s'\n", ast_channel_name(ast), target_context);
} else {
- ast_channel_lock(ast);
- ast_mutex_lock(&p->lock);
ast_log(LOG_NOTICE, "Fax detected, but no fax extension\n");
}
+ ast_channel_lock(ast);
+ ast_mutex_lock(&p->lock);
} else {
ast_debug(1, "Already in a fax extension, not redirecting\n");
}
@@ -8643,6 +8700,15 @@ static struct ast_frame *dahdi_read(struct ast_channel *ast)
/* Perform busy detection etc on the dahdi line */
int mute;
+ if ((p->dsp_features & DSP_FEATURE_FAX_DETECT)
+ && p->faxdetect_timeout
+ && p->faxdetect_timeout <= ast_channel_get_up_time(ast)) {
+ p->dsp_features &= ~DSP_FEATURE_FAX_DETECT;
+ ast_dsp_set_features(p->dsp, p->dsp_features);
+ ast_debug(1, "Channel driver fax CNG detection timeout on %s\n",
+ ast_channel_name(ast));
+ }
+
f = ast_dsp_process(ast, p->dsp, &p->subs[idx].f);
/* Check if DSP code thinks we should be muting this frame and mute the conference if so */
@@ -12542,6 +12608,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
tmp->callprogress = conf->chan.callprogress;
tmp->waitfordialtone = conf->chan.waitfordialtone;
tmp->dialtone_detect = conf->chan.dialtone_detect;
+ tmp->faxdetect_timeout = conf->chan.faxdetect_timeout;
tmp->cancallforward = conf->chan.cancallforward;
tmp->dtmfrelax = conf->chan.dtmfrelax;
tmp->callwaiting = tmp->permcallwaiting;
@@ -17793,6 +17860,10 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
confp->chan.callprogress |= CALLPROGRESS_FAX_OUTGOING;
} else if (!strcasecmp(v->value, "both") || ast_true(v->value))
confp->chan.callprogress |= CALLPROGRESS_FAX_INCOMING | CALLPROGRESS_FAX_OUTGOING;
+ } else if (!strcasecmp(v->name, "faxdetect_timeout")) {
+ if (sscanf(v->value, "%30u", &confp->chan.faxdetect_timeout) != 1) {
+ confp->chan.faxdetect_timeout = 0;
+ }
} else if (!strcasecmp(v->name, "echocancel")) {
process_echocancel(confp, v->value, v->lineno);
} else if (!strcasecmp(v->name, "echotraining")) {
diff --git a/channels/chan_dahdi.h b/channels/chan_dahdi.h
index 4bb5d19a2..ab5c1eba9 100644
--- a/channels/chan_dahdi.h
+++ b/channels/chan_dahdi.h
@@ -612,6 +612,11 @@ struct dahdi_pvt {
*/
int dialtone_detect;
int dialtone_scanning_time_elapsed; /*!< Amount of audio scanned for dialtone, in frames */
+ /*!
+ * \brief The number of seconds into call to disable fax detection. (0 = disabled)
+ * \note Set from the "faxdetect_timeout" value read in from chan_dahdi.conf
+ */
+ unsigned int faxdetect_timeout;
struct timeval waitingfordt; /*!< Time we started waiting for dialtone */
struct timeval flashtime; /*!< Last flash-hook time */
/*! \brief Opaque DSP configuration structure. */
diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c
index 8d5018d2c..bd773fc84 100644
--- a/channels/chan_iax2.c
+++ b/channels/chan_iax2.c
@@ -213,6 +213,25 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>Gets or sets a variable that is sent to a remote IAX2 peer during call setup.</para>
</description>
</function>
+ <info name="IAXCHANNEL" language="en_US" tech="IAX">
+ <enumlist>
+ <enum name="osptoken">
+ <para>R/O Get the peer's osptoken.</para>
+ </enum>
+ <enum name="peerip">
+ <para>R/O Get the peer's ip address.</para>
+ </enum>
+ <enum name="peername">
+ <para>R/O Get the peer's username.</para>
+ </enum>
+ <enum name="secure_signaling">
+ <para>R/O Get the if the IAX channel is secured.</para>
+ </enum>
+ <enum name="secure_media">
+ <para>R/O Get the if the IAX channel is secured.</para>
+ </enum>
+ </enumlist>
+ </info>
<manager name="IAXpeers" language="en_US">
<synopsis>
List IAX peers.
diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c
index 970fef496..82f716f08 100644
--- a/channels/chan_pjsip.c
+++ b/channels/chan_pjsip.c
@@ -612,10 +612,12 @@ static struct ast_frame *chan_pjsip_cng_tone_detected(struct ast_sip_session *se
{
const char *target_context;
int exists;
+ int dsp_features;
- /* If we only needed this DSP for fax detection purposes we can just drop it now */
- 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);
+ dsp_features = ast_dsp_get_features(session->dsp);
+ dsp_features &= ~DSP_FEATURE_FAX_DETECT;
+ if (dsp_features) {
+ ast_dsp_set_features(session->dsp, dsp_features);
} else {
ast_dsp_free(session->dsp);
session->dsp = NULL;
@@ -628,16 +630,19 @@ static struct ast_frame *chan_pjsip_cng_tone_detected(struct ast_sip_session *se
target_context = S_OR(ast_channel_macrocontext(session->channel), ast_channel_context(session->channel));
- /* We need to unlock the channel here because ast_exists_extension has the
+ /*
+ * We need to unlock the channel here because ast_exists_extension has the
* potential to start and stop an autoservice on the channel. Such action
* is prone to deadlock if the channel is locked.
+ *
+ * ast_async_goto() has its own restriction on not holding the channel lock.
*/
ast_channel_unlock(session->channel);
+ ast_frfree(f);
+ f = &ast_null_frame;
exists = ast_exists_extension(session->channel, target_context, "fax", 1,
S_COR(ast_channel_caller(session->channel)->id.number.valid,
ast_channel_caller(session->channel)->id.number.str, NULL));
- ast_channel_lock(session->channel);
-
if (exists) {
ast_verb(2, "Redirecting '%s' to fax extension due to CNG detection\n",
ast_channel_name(session->channel));
@@ -646,12 +651,11 @@ static struct ast_frame *chan_pjsip_cng_tone_detected(struct ast_sip_session *se
ast_log(LOG_ERROR, "Failed to async goto '%s' into fax extension in '%s'\n",
ast_channel_name(session->channel), target_context);
}
- ast_frfree(f);
- f = &ast_null_frame;
} else {
ast_log(LOG_NOTICE, "FAX CNG detected on '%s' but no fax extension in '%s'\n",
ast_channel_name(session->channel), target_context);
}
+ ast_channel_lock(session->channel);
return f;
}
@@ -660,6 +664,7 @@ static struct ast_frame *chan_pjsip_cng_tone_detected(struct ast_sip_session *se
static struct ast_frame *chan_pjsip_read(struct ast_channel *ast)
{
struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(ast);
+ struct ast_sip_session *session;
struct chan_pjsip_pvt *pvt = channel->pvt;
struct ast_frame *f;
struct ast_sip_session_media *media = NULL;
@@ -697,22 +702,42 @@ static struct ast_frame *chan_pjsip_read(struct ast_channel *ast)
return f;
}
- if (ast_format_cap_iscompatible_format(channel->session->endpoint->media.codecs, f->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
+ session = channel->session;
+
+ if (ast_format_cap_iscompatible_format(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_sorcery_object_get_id(session->endpoint));
ast_frfree(f);
return &ast_null_frame;
}
- if (channel->session->dsp) {
- f = ast_dsp_process(ast, channel->session->dsp, f);
+ if (session->dsp) {
+ int dsp_features;
+ dsp_features = ast_dsp_get_features(session->dsp);
+ if ((dsp_features & DSP_FEATURE_FAX_DETECT)
+ && session->endpoint->faxdetect_timeout
+ && session->endpoint->faxdetect_timeout <= ast_channel_get_up_time(ast)) {
+ dsp_features &= ~DSP_FEATURE_FAX_DETECT;
+ if (dsp_features) {
+ ast_dsp_set_features(session->dsp, dsp_features);
+ } else {
+ ast_dsp_free(session->dsp);
+ session->dsp = NULL;
+ }
+ ast_debug(3, "Channel driver fax CNG detection timeout on %s\n",
+ ast_channel_name(ast));
+ }
+ }
+ if (session->dsp) {
+ f = ast_dsp_process(ast, session->dsp, f);
if (f && (f->frametype == AST_FRAME_DTMF)) {
if (f->subclass.integer == 'f') {
- ast_debug(3, "Fax CNG detected on %s\n", ast_channel_name(ast));
- f = chan_pjsip_cng_tone_detected(channel->session, f);
+ ast_debug(3, "Channel driver fax CNG detected on %s\n",
+ ast_channel_name(ast));
+ f = chan_pjsip_cng_tone_detected(session, f);
} else {
ast_debug(3, "* Detected inband DTMF '%c' on '%s'\n", f->subclass.integer,
ast_channel_name(ast));
@@ -1417,7 +1442,8 @@ static void transfer_redirect(struct ast_sip_session *session, const char *targe
pjsip_contact_hdr *contact;
pj_str_t tmp;
- if (pjsip_inv_end_session(session->inv_session, 302, NULL, &packet) != PJ_SUCCESS) {
+ if (pjsip_inv_end_session(session->inv_session, 302, NULL, &packet) != PJ_SUCCESS
+ || !packet) {
ast_log(LOG_WARNING, "Failed to redirect PJSIP session for channel %s\n",
ast_channel_name(session->channel));
message = AST_TRANSFER_FAILED;
@@ -2182,7 +2208,8 @@ static int chan_pjsip_incoming_request(struct ast_sip_session *session, struct p
ast_sip_session_add_datastore(session, datastore);
if (!(session->channel = chan_pjsip_new(session, AST_STATE_RING, session->exten, NULL, NULL, NULL, NULL))) {
- if (pjsip_inv_end_session(session->inv_session, 503, NULL, &packet) == PJ_SUCCESS) {
+ if (pjsip_inv_end_session(session->inv_session, 503, NULL, &packet) == PJ_SUCCESS
+ && packet) {
ast_sip_session_send_response(session, packet);
}
@@ -2357,6 +2384,11 @@ static struct ast_custom_function media_offer_function = {
.write = pjsip_acf_media_offer_write
};
+static struct ast_custom_function session_refresh_function = {
+ .name = "PJSIP_SEND_SESSION_REFRESH",
+ .write = pjsip_acf_session_refresh_write,
+};
+
/*!
* \brief Load the module
*
@@ -2396,6 +2428,11 @@ static int load_module(void)
goto end;
}
+ if (ast_custom_function_register(&session_refresh_function)) {
+ ast_log(LOG_WARNING, "Unable to register PJSIP_SEND_SESSION_REFRESH dialplan function\n");
+ goto end;
+ }
+
if (ast_sip_session_register_supplement(&chan_pjsip_supplement)) {
ast_log(LOG_ERROR, "Unable to register PJSIP supplement\n");
goto end;
@@ -2452,6 +2489,7 @@ end:
pjsip_uids_onhold = NULL;
ast_custom_function_unregister(&media_offer_function);
ast_custom_function_unregister(&chan_pjsip_dial_contacts_function);
+ ast_custom_function_unregister(&session_refresh_function);
ast_channel_unregister(&chan_pjsip_tech);
ast_rtp_glue_unregister(&chan_pjsip_rtp_glue);
@@ -2473,6 +2511,7 @@ static int unload_module(void)
ast_custom_function_unregister(&media_offer_function);
ast_custom_function_unregister(&chan_pjsip_dial_contacts_function);
+ ast_custom_function_unregister(&session_refresh_function);
ast_channel_unregister(&chan_pjsip_tech);
ao2_ref(chan_pjsip_tech.capabilities, -1);
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 8f3296adf..9cc6252fd 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -4200,19 +4200,6 @@ static enum sip_result __sip_reliable_xmit(struct sip_pvt *p, uint32_t seqno, in
p->pendinginvite = seqno;
}
- /* If the transport is something reliable (TCP or TLS) then don't really send this reliably */
- /* I removed the code from retrans_pkt that does the same thing so it doesn't get loaded into the scheduler */
- /*! \todo According to the RFC some packets need to be retransmitted even if its TCP, so this needs to get revisited */
- if (!(p->socket.type & AST_TRANSPORT_UDP)) {
- xmitres = __sip_xmit(p, data); /* Send packet */
- if (xmitres == XMIT_ERROR) { /* Serious network trouble, no need to try again */
- append_history(p, "XmitErr", "%s", fatal ? "(Critical)" : "(Non-critical)");
- return AST_FAILURE;
- } else {
- return AST_SUCCESS;
- }
- }
-
pkt = ao2_alloc_options(sizeof(*pkt), sip_pkt_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK);
if (!pkt) {
return AST_FAILURE;
@@ -4249,6 +4236,10 @@ static enum sip_result __sip_reliable_xmit(struct sip_pvt *p, uint32_t seqno, in
pkt->time_sent = ast_tvnow(); /* time packet was sent */
pkt->retrans_stop_time = 64 * (pkt->timer_t1 ? pkt->timer_t1 : DEFAULT_TIMER_T1); /* time in ms after pkt->time_sent to stop retransmission */
+ if (!(p->socket.type & AST_TRANSPORT_UDP)) {
+ pkt->retrans_stop = 1;
+ }
+
/* Schedule retransmission */
ao2_t_ref(pkt, +1, "Schedule packet retransmission");
pkt->retransid = ast_sched_add_variable(sched, siptimer_a, retrans_pkt, pkt, 1);
@@ -7665,7 +7656,8 @@ static int interpret_t38_parameters(struct sip_pvt *p, const struct ast_control_
ast_udptl_set_local_max_ifp(p->udptl, p->t38.our_parms.max_ifp);
change_t38_state(p, T38_ENABLED);
transmit_response_with_t38_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL);
- } else if (p->t38.state != T38_ENABLED) {
+ } else if ((p->t38.state != T38_ENABLED) || ((p->t38.state == T38_ENABLED) &&
+ (parameters->request_response == AST_T38_REQUEST_NEGOTIATE))) {
p->t38.our_parms = *parameters;
ast_udptl_set_local_max_ifp(p->udptl, p->t38.our_parms.max_ifp);
change_t38_state(p, T38_LOCAL_REINVITE);
@@ -8606,29 +8598,31 @@ static struct ast_frame *sip_read(struct ast_channel *ast)
if (faxdetected && ast_test_flag(&p->flags[1], SIP_PAGE2_FAX_DETECT_CNG)) {
if (strcmp(ast_channel_exten(ast), "fax")) {
const char *target_context = S_OR(ast_channel_macrocontext(ast), ast_channel_context(ast));
- /* We need to unlock 'ast' here because
+ /*
+ * We need to unlock 'ast' here because
* ast_exists_extension has the potential to start and
* stop an autoservice on the channel. Such action is
* prone to deadlock if the channel is locked.
+ *
+ * ast_async_goto() has its own restriction on not holding
+ * the channel lock.
*/
sip_pvt_unlock(p);
ast_channel_unlock(ast);
+ ast_frfree(fr);
+ fr = &ast_null_frame;
if (ast_exists_extension(ast, target_context, "fax", 1,
S_COR(ast_channel_caller(ast)->id.number.valid, ast_channel_caller(ast)->id.number.str, NULL))) {
- ast_channel_lock(ast);
- sip_pvt_lock(p);
ast_verb(2, "Redirecting '%s' to fax extension due to CNG detection\n", ast_channel_name(ast));
pbx_builtin_setvar_helper(ast, "FAXEXTEN", ast_channel_exten(ast));
if (ast_async_goto(ast, target_context, "fax", 1)) {
ast_log(LOG_NOTICE, "Failed to async goto '%s' into fax of '%s'\n", ast_channel_name(ast), target_context);
}
- ast_frfree(fr);
- fr = &ast_null_frame;
} else {
- ast_channel_lock(ast);
- sip_pvt_lock(p);
ast_log(LOG_NOTICE, "FAX CNG detected but no fax extension\n");
}
+ ast_channel_lock(ast);
+ sip_pvt_lock(p);
}
}
@@ -11334,25 +11328,7 @@ static int process_sdp_a_audio(const char *a, struct sip_pvt *p, struct ast_rtp_
ast_rtp_codecs_payloads_unset(newaudiortp, NULL, codec);
}
- if (ast_format_cmp(format, ast_format_siren7) == AST_FORMAT_CMP_EQUAL) {
- if (sscanf(fmtp_string, "bitrate=%30u", &bit_rate) == 1) {
- if (bit_rate != 32000) {
- ast_log(LOG_WARNING, "Got Siren7 offer at %u bps, but only 32000 bps supported; ignoring.\n", bit_rate);
- ast_rtp_codecs_payloads_unset(newaudiortp, NULL, codec);
- } else {
- found = TRUE;
- }
- }
- } else if (ast_format_cmp(format, ast_format_siren14) == AST_FORMAT_CMP_EQUAL) {
- if (sscanf(fmtp_string, "bitrate=%30u", &bit_rate) == 1) {
- if (bit_rate != 48000) {
- ast_log(LOG_WARNING, "Got Siren14 offer at %u bps, but only 48000 bps supported; ignoring.\n", bit_rate);
- ast_rtp_codecs_payloads_unset(newaudiortp, NULL, codec);
- } else {
- found = TRUE;
- }
- }
- } else if (ast_format_cmp(format, ast_format_g719) == AST_FORMAT_CMP_EQUAL) {
+ if (ast_format_cmp(format, ast_format_g719) == AST_FORMAT_CMP_EQUAL) {
if (sscanf(fmtp_string, "bitrate=%30u", &bit_rate) == 1) {
if (bit_rate != 64000) {
ast_log(LOG_WARNING, "Got G.719 offer at %u bps, but only 64000 bps supported; ignoring.\n", bit_rate);
@@ -13011,12 +12987,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_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);
- } else if (ast_format_cmp(format, ast_format_siren14) == AST_FORMAT_CMP_EQUAL) {
- /* Indicate that we only expect 48Kbps */
- ast_str_append(a_buf, 0, "a=fmtp:%d bitrate=48000\r\n", rtp_code);
} else if (ast_format_cmp(format, ast_format_g719) == AST_FORMAT_CMP_EQUAL) {
/* Indicate that we only expect 64Kbps */
ast_str_append(a_buf, 0, "a=fmtp:%d bitrate=64000\r\n", rtp_code);
@@ -14156,9 +14126,10 @@ static void build_contact(struct sip_pvt *p, struct sip_request *req, int incomi
/*! \brief Initiate new SIP request to peer/user */
static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod, const char * const explicit_uri)
{
- struct ast_str *invite = ast_str_alloca(256);
- char from[256];
- char to[256];
+#define SIPHEADER 256
+ struct ast_str *invite = ast_str_create(SIPHEADER);
+ struct ast_str *from = ast_str_create(SIPHEADER);
+ struct ast_str *to = ast_str_create(SIPHEADER);
char tmp_n[SIPBUFSIZE/2]; /* build a local copy of 'n' if needed */
char tmp_l[SIPBUFSIZE/2]; /* build a local copy of 'l' if needed */
const char *l = NULL; /* XXX what is this, exactly ? */
@@ -14260,34 +14231,40 @@ static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmetho
ourport = (p->fromdomainport && (p->fromdomainport != STANDARD_SIP_PORT)) ? p->fromdomainport : ast_sockaddr_port(&p->ourip);
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);
+ ret = ast_str_set(&from, 0, "<sip:%s@%s:%d>;tag=%s", tmp_l, d, ourport, p->tag);
} else {
- ret = snprintf(from, sizeof(from), "<sip:%s@%s>;tag=%s", tmp_l, d, p->tag);
+ ret = ast_str_set(&from, 0, "<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 */
+ if (ret == AST_DYNSTR_BUILD_FAILED) {
/* 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);
+ /* Make sure that the field contains something non-broken.
+ See https://issues.asterisk.org/jira/browse/ASTERISK-26069
+ */
+ ast_str_set(&from, 3, "<>");
+
}
/* 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] = ' ';
+ size_t written = ast_str_strlen(from);
+ size_t name_len;
+ if (sip_cfg.pedanticsipchecking) {
+ ast_escape_quoted(n, tmp_n, sizeof(tmp_n));
+ n = tmp_n;
+ }
+ name_len = strlen(n);
+ ret = ast_str_make_space(&from, name_len + written + 4);
+
+ if (ret == 0) {
+ /* needed again, as ast_str_make_space coud've changed the pointer */
+ char *from_buf = ast_str_buffer(from);
+
+ memmove(from_buf + name_len + 3, from_buf, written + 1);
+ from_buf[0] = '"';
+ memcpy(from_buf + 1, n, name_len);
+ from_buf[name_len + 1] = '"';
+ from_buf[name_len + 2] = ' ';
}
}
@@ -14330,24 +14307,28 @@ 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 */
- ret = snprintf(to, sizeof(to), "<sip:%s@%s>%s%s", p->todnid, p->tohost, ast_strlen_zero(p->theirtag) ? "" : ";tag=", p->theirtag);
+ ret = ast_str_set(&to, 0, "<sip:%s@%s>%s%s", p->todnid, p->tohost, ast_strlen_zero(p->theirtag) ? "" : ";tag=", p->theirtag);
} else {
- ret = snprintf(to, sizeof(to), "<sip:%s>%s%s", p->todnid, ast_strlen_zero(p->theirtag) ? "" : ";tag=", p->theirtag);
+ ret = ast_str_set(&to, 0, "<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) */
- ret = snprintf(to, sizeof(to), "<%s%s>;tag=%s", (strncasecmp(p->uri, "sip:", 4) ? "sip:" : ""), p->uri, p->theirtag);
+ ret = ast_str_set(&to, 0, "<%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 */
- ret = snprintf(to, sizeof(to), "<%s>;%s", p->uri, p->options->vxml_url);
+ ret = ast_str_set(&to, 0, "<%s>;%s", p->uri, p->options->vxml_url);
} else {
- ret = snprintf(to, sizeof(to), "<%s>", p->uri);
+ ret = ast_str_set(&to, 0, "<%s>", p->uri);
}
}
- if (ret < 0 || ret >= sizeof(to)) { /* a return value of size or more means that the output was truncated */
+ if (ret == AST_DYNSTR_BUILD_FAILED) {
/* 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);
+ /* Make sure that the field contains something non-broken.
+ See https://issues.asterisk.org/jira/browse/ASTERISK-26069
+ */
+ ast_str_set(&to, 3, "<>");
}
init_req(req, sipmethod, p->uri);
@@ -14362,8 +14343,8 @@ static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmetho
*/
add_route(req, &p->route, 0);
- add_header(req, "From", from);
- add_header(req, "To", to);
+ add_header(req, "From", ast_str_buffer(from));
+ add_header(req, "To", ast_str_buffer(to));
ast_string_field_set(p, exten, l);
build_contact(p, req, 0);
add_header(req, "Contact", p->our_contact);
@@ -14372,6 +14353,10 @@ static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmetho
if (!ast_strlen_zero(global_useragent)) {
add_header(req, "User-Agent", global_useragent);
}
+
+ ast_free(from);
+ ast_free(to);
+ ast_free(invite);
}
/*! \brief Add "Diversion" header to outgoing message
@@ -17196,10 +17181,8 @@ static void mwi_event_cb(void *userdata, struct stasis_subscription *sub, struct
struct sip_peer *peer = sip_find_peer(peer_name, NULL, TRUE, FINDALLDEVICES, FALSE, 0);
if (stasis_subscription_final_message(sub, msg)) {
- if (peer) {
- /* configuration reloaded */
- return;
- }
+ /* peer can be non-NULL during reload. */
+ ao2_cleanup(peer);
ast_free(peer_name);
return;
}
@@ -18353,7 +18336,7 @@ static enum sip_get_dest_result get_destination(struct sip_pvt *p, struct sip_re
static int get_sip_pvt_from_replaces(const char *callid, const char *totag,
const char *fromtag, struct sip_pvt **out_pvt, struct ast_channel **out_chan)
{
- struct sip_pvt *sip_pvt_ptr;
+ RAII_VAR(struct sip_pvt *, sip_pvt_ptr, NULL, ao2_cleanup);
struct sip_pvt tmp_dialog = {
.callid = callid,
};
@@ -18428,6 +18411,9 @@ static int get_sip_pvt_from_replaces(const char *callid, const char *totag,
}
}
+ /* If we're here sip_pvt_ptr has been copied to *out_pvt, prevent RAII_VAR cleanup */
+ sip_pvt_ptr = NULL;
+
return 0;
}
@@ -21287,15 +21273,13 @@ static char *sip_unregister(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
}
/*! \brief Callback for show_chanstats */
-static int show_chanstats_cb(void *__cur, void *__arg, int flags)
+static int show_chanstats_cb(struct sip_pvt *cur, struct __show_chan_arg *arg)
{
#define FORMAT2 "%-15.15s %-11.11s %-8.8s %-10.10s %-10.10s ( %%) %-6.6s %-10.10s %-10.10s ( %%) %-6.6s\n"
#define FORMAT "%-15.15s %-11.11s %-8.8s %-10.10u%-1.1s %-10.10u (%5.2f%%) %-6.4lf %-10.10u%-1.1s %-10.10u (%5.2f%%) %-6.4lf\n"
- struct sip_pvt *cur = __cur;
struct ast_rtp_instance_stats stats;
char durbuf[10];
struct ast_channel *c;
- struct __show_chan_arg *arg = __arg;
int fd = arg->fd;
sip_pvt_lock(cur);
@@ -21355,6 +21339,8 @@ static int show_chanstats_cb(void *__cur, void *__arg, int flags)
static char *sip_show_channelstats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
struct __show_chan_arg arg = { .fd = a->fd, .numchans = 0 };
+ struct sip_pvt *cur;
+ struct ao2_iterator i;
switch (cmd) {
case CLI_INIT:
@@ -21372,8 +21358,14 @@ static char *sip_show_channelstats(struct ast_cli_entry *e, int cmd, struct ast_
return CLI_SHOWUSAGE;
ast_cli(a->fd, FORMAT2, "Peer", "Call ID", "Duration", "Recv: Pack", "Lost", "Jitter", "Send: Pack", "Lost", "Jitter");
+
/* iterate on the container and invoke the callback on each item */
- ao2_t_callback(dialogs, OBJ_NODATA, show_chanstats_cb, &arg, "callback to sip show chanstats");
+ i = ao2_iterator_init(dialogs, 0);
+ for (; (cur = ao2_iterator_next(&i)); ao2_ref(cur, -1)) {
+ show_chanstats_cb(cur, &arg);
+ }
+ ao2_iterator_destroy(&i);
+
ast_cli(a->fd, "%d active SIP channel%s\n", arg.numchans, (arg.numchans != 1) ? "s" : "");
return CLI_SUCCESS;
}
@@ -21689,10 +21681,8 @@ static const struct cfsubscription_types *find_subscription_type(enum subscripti
#define FORMAT "%-15.15s %-15.15s %-15.15s %-15.15s %-3.3s %-3.3s %-15.15s %-10.10s %-10.10s\n"
/*! \brief callback for show channel|subscription */
-static int show_channels_cb(void *__cur, void *__arg, int flags)
+static int show_channels_cb(struct sip_pvt *cur, struct __show_chan_arg *arg)
{
- struct sip_pvt *cur = __cur;
- struct __show_chan_arg *arg = __arg;
const struct ast_sockaddr *dst;
sip_pvt_lock(cur);
@@ -21744,7 +21734,8 @@ static int show_channels_cb(void *__cur, void *__arg, int flags)
static char *sip_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
struct __show_chan_arg arg = { .fd = a->fd, .numchans = 0 };
-
+ struct sip_pvt *cur;
+ struct ao2_iterator i;
if (cmd == CLI_INIT) {
e->command = "sip show {channels|subscriptions}";
@@ -21766,7 +21757,11 @@ static char *sip_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_
ast_cli(arg.fd, FORMAT3, "Peer", "User", "Call ID", "Extension", "Last state", "Type", "Mailbox", "Expiry");
/* iterate on the container and invoke the callback on each item */
- ao2_t_callback(dialogs, OBJ_NODATA, show_channels_cb, &arg, "callback to show channels");
+ i = ao2_iterator_init(dialogs, 0);
+ for (; (cur = ao2_iterator_next(&i)); ao2_ref(cur, -1)) {
+ show_channels_cb(cur, &arg);
+ }
+ ao2_iterator_destroy(&i);
/* print summary information */
ast_cli(arg.fd, "%d active SIP %s%s\n", arg.numchans,
@@ -24590,6 +24585,7 @@ static void handle_response(struct sip_pvt *p, int resp, const char *rest, struc
char *c_copy = ast_strdupa(c);
/* Skip the Cseq and its subsequent spaces */
const char *msg = ast_skip_blanks(ast_skip_nonblanks(c_copy));
+ int ack_res = FALSE;
if (!msg)
msg = "";
@@ -24618,28 +24614,24 @@ static void handle_response(struct sip_pvt *p, int resp, const char *rest, struc
ast_channel_hangupcause_set(owner, hangup_sip2cause(resp));
}
- if (p->socket.type == AST_TRANSPORT_UDP) {
- int ack_res = FALSE;
-
- /* Acknowledge whatever it is destined for */
- if ((resp >= 100) && (resp <= 199)) {
- /* NON-INVITE messages do not ack a 1XX response. RFC 3261 section 17.1.2.2 */
- if (sipmethod == SIP_INVITE) {
- ack_res = __sip_semi_ack(p, seqno, 0, sipmethod);
- }
- } else {
- ack_res = __sip_ack(p, seqno, 0, sipmethod);
+ /* Acknowledge whatever it is destined for */
+ if ((resp >= 100) && (resp <= 199)) {
+ /* NON-INVITE messages do not ack a 1XX response. RFC 3261 section 17.1.2.2 */
+ if (sipmethod == SIP_INVITE) {
+ ack_res = __sip_semi_ack(p, seqno, 0, sipmethod);
}
+ } else {
+ ack_res = __sip_ack(p, seqno, 0, sipmethod);
+ }
- if (ack_res == FALSE) {
- /* RFC 3261 13.2.2.4 and 17.1.1.2 - We must re-send ACKs to re-transmitted final responses */
- if (sipmethod == SIP_INVITE && resp >= 200) {
- transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, resp < 300 ? TRUE: FALSE);
- }
-
- append_history(p, "Ignore", "Ignoring this retransmit\n");
- return;
+ if (ack_res == FALSE) {
+ /* RFC 3261 13.2.2.4 and 17.1.1.2 - We must re-send ACKs to re-transmitted final responses */
+ if (sipmethod == SIP_INVITE && resp >= 200) {
+ transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, resp < 300 ? TRUE: FALSE);
}
+
+ append_history(p, "Ignore", "Ignoring this retransmit\n");
+ return;
}
/* If this is a NOTIFY for a subscription clear the flag that indicates that we have a NOTIFY pending */
@@ -31041,6 +31033,7 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
return NULL;
}
if (!(peer->endpoint = ast_endpoint_create("SIP", name))) {
+ ao2_t_ref(peer, -1, "failed to allocate endpoint, drop peer");
return NULL;
}
if (!(peer->caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {
diff --git a/channels/chan_unistim.c b/channels/chan_unistim.c
index db4720d1a..37281bb48 100644
--- a/channels/chan_unistim.c
+++ b/channels/chan_unistim.c
@@ -567,8 +567,10 @@ static const unsigned char packet_send_stream_based_tone_off[] =
{ 0x16, 0x05, 0x1c, 0x00, 0x00 };
static const unsigned char packet_send_mute[] = { 0x16, 0x05, 0x04, 0x00, 0x00 };
+#ifdef NOT_USED
static const unsigned char packet_send_CloseAudioStreamRX[] = { 0x16, 0x05, 0x31, 0x00, 0xff };
static const unsigned char packet_send_CloseAudioStreamTX[] = { 0x16, 0x05, 0x31, 0xff, 0x00 };
+#endif
static const unsigned char packet_send_stream_based_tone_on[] =
{ 0x16, 0x06, 0x1b, 0x00, 0x00, 0x05 };
static const unsigned char packet_send_stream_based_tone_single_freq[] =
@@ -1021,7 +1023,7 @@ static int get_to_address(int fd, struct sockaddr_in *toAddr)
memcpy(&toAddr->sin_addr, &ip_msg.address, sizeof(struct in_addr));
return err;
#else
- memcpy(&toAddr, &public_ip, sizeof(&toAddr));
+ memcpy(toAddr, &public_ip, sizeof(*toAddr));
return 0;
#endif
}
diff --git a/channels/pjsip/dialplan_functions.c b/channels/pjsip/dialplan_functions.c
index b5665039a..380a05eed 100644
--- a/channels/pjsip/dialplan_functions.c
+++ b/channels/pjsip/dialplan_functions.c
@@ -59,8 +59,55 @@
</parameter>
</syntax>
<description>
- <para>Returns the codecs offered based upon the media choice</para>
+ <para>When read, returns the codecs offered based upon the media choice.</para>
+ <para>When written, sets the codecs to offer when an outbound dial attempt is made,
+ or when a session refresh is sent using <replaceable>PJSIP_SEND_SESSION_REFRESH</replaceable>.
+ </para>
</description>
+ <see-also>
+ <ref type="function">PJSIP_SEND_SESSION_REFRESH</ref>
+ </see-also>
+</function>
+<function name="PJSIP_SEND_SESSION_REFRESH" language="en_US">
+ <synopsis>
+ W/O: Initiate a session refresh via an UPDATE or re-INVITE on an established media session
+ </synopsis>
+ <syntax>
+ <parameter name="update_type" required="false">
+ <para>The type of update to send. Default is <literal>invite</literal>.</para>
+ <enumlist>
+ <enum name="invite">
+ <para>Send the session refresh as a re-INVITE.</para>
+ </enum>
+ <enum name="update">
+ <para>Send the session refresh as an UPDATE.</para>
+ </enum>
+ </enumlist>
+ </parameter>
+ </syntax>
+ <description>
+ <para>This function will cause the PJSIP stack to immediately refresh
+ the media session for the channel. This will be done using either a
+ re-INVITE (default) or an UPDATE request.
+ </para>
+ <para>This is most useful when combined with the <replaceable>PJSIP_MEDIA_OFFER</replaceable>
+ dialplan function, as it allows the formats in use on a channel to be
+ re-negotiated after call setup.</para>
+ <warning>
+ <para>The formats the endpoint supports are <emphasis>not</emphasis>
+ checked or enforced by this function. Using this function to offer
+ formats not supported by the endpoint <emphasis>may</emphasis> result
+ in a loss of media.</para>
+ </warning>
+ <example title="Re-negotiate format to g722">
+ ; Within some existing extension on an answered channel
+ same => n,Set(PJSIP_MEDIA_OFFER(audio)=!all,g722)
+ same => n,Set(PJSIP_SEND_SESSION_REFRESH()=invite)
+ </example>
+ </description>
+ <see-also>
+ <ref type="function">PJSIP_MEDIA_OFFER</ref>
+ </see-also>
</function>
<info name="PJSIPCHANNEL" language="en_US" tech="PJSIP">
<enumlist>
@@ -364,6 +411,19 @@
</enum>
</enumlist>
</info>
+<info name="PJSIPCHANNEL_EXAMPLES" language="en_US" tech="PJSIP">
+ <example title="PJSIP specific CHANNEL examples">
+ ; Log the current Call-ID
+ same => n,Log(NOTICE, ${CHANNEL(pjsip,call-id)})
+
+ ; Log the destination address of the audio stream
+ same => n,Log(NOTICE, ${CHANNEL(rtp,dest)})
+
+ ; Store the round-trip time associated with a
+ ; video stream in the CDR field video-rtt
+ same => n,Set(CDR(video-rtt)=${CHANNEL(rtcp,rtt,video)})
+ </example>
+</info>
***/
#include "asterisk.h"
@@ -961,3 +1021,70 @@ int pjsip_acf_media_offer_write(struct ast_channel *chan, const char *cmd, char
return ast_sip_push_task_synchronous(channel->session->serializer, media_offer_write_av, &mdata);
}
+
+struct refresh_data {
+ struct ast_sip_session *session;
+ enum ast_sip_session_refresh_method method;
+};
+
+static int sip_session_response_cb(struct ast_sip_session *session, pjsip_rx_data *rdata)
+{
+ struct ast_format *fmt;
+
+ if (!session->channel) {
+ /* Egads! */
+ return 0;
+ }
+
+ fmt = ast_format_cap_get_best_by_type(ast_channel_nativeformats(session->channel), AST_MEDIA_TYPE_AUDIO);
+ if (!fmt) {
+ /* No format? That's weird. */
+ return 0;
+ }
+ ast_channel_set_writeformat(session->channel, fmt);
+ ast_channel_set_rawwriteformat(session->channel, fmt);
+ ast_channel_set_readformat(session->channel, fmt);
+ ast_channel_set_rawreadformat(session->channel, fmt);
+ ao2_ref(fmt, -1);
+
+ return 0;
+}
+
+static int refresh_write_cb(void *obj)
+{
+ struct refresh_data *data = obj;
+
+ ast_sip_session_refresh(data->session, NULL, NULL,
+ sip_session_response_cb, data->method, 1);
+
+ return 0;
+}
+
+int pjsip_acf_session_refresh_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
+{
+ struct ast_sip_channel_pvt *channel;
+ struct refresh_data rdata = {
+ .method = AST_SIP_SESSION_REFRESH_METHOD_INVITE,
+ };
+
+ if (!chan) {
+ ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
+ return -1;
+ }
+
+ if (strcmp(ast_channel_tech(chan)->type, "PJSIP")) {
+ ast_log(LOG_WARNING, "Cannot call %s on a non-PJSIP channel\n", cmd);
+ return -1;
+ }
+
+ channel = ast_channel_tech_pvt(chan);
+ rdata.session = channel->session;
+
+ if (!strcmp(value, "invite")) {
+ rdata.method = AST_SIP_SESSION_REFRESH_METHOD_INVITE;
+ } else if (!strcmp(value, "update")) {
+ rdata.method = AST_SIP_SESSION_REFRESH_METHOD_UPDATE;
+ }
+
+ return ast_sip_push_task_synchronous(channel->session->serializer, refresh_write_cb, &rdata);
+}
diff --git a/channels/pjsip/include/dialplan_functions.h b/channels/pjsip/include/dialplan_functions.h
index cbc06f076..8b80bfa74 100644
--- a/channels/pjsip/include/dialplan_functions.h
+++ b/channels/pjsip/include/dialplan_functions.h
@@ -61,6 +61,18 @@ int pjsip_acf_media_offer_write(struct ast_channel *chan, const char *cmd, char
int pjsip_acf_media_offer_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len);
/*!
+ * \brief PJSIP_SEND_SESSION_REFRESH function write callback
+ * \param chan The channel the function is called on
+ * \param cmd the Name of the function
+ * \param data Arguments passed to the function
+ * \param value Value to be set by the function
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int pjsip_acf_session_refresh_write(struct ast_channel *chan, const char *cmd, char *data, const char *value);
+
+/*!
* \brief PJSIP_DIAL_CONTACTS function read callback
* \param chan The channel the function is called on
* \param cmd The name of the function
diff --git a/channels/sip/config_parser.c b/channels/sip/config_parser.c
index 50495427e..0feb24655 100644
--- a/channels/sip/config_parser.c
+++ b/channels/sip/config_parser.c
@@ -274,6 +274,7 @@ int sip_parse_register_line(struct sip_registry *reg, int default_expiry, const
return 0;
}
+#ifdef TEST_FRAMEWORK
AST_TEST_DEFINE(sip_parse_register_line_test)
{
int res = AST_TEST_PASS;
@@ -643,6 +644,7 @@ alloc_fail:
ast_test_status_update(test, "Out of memory. \n");
return res;
}
+#endif
int sip_parse_host(char *line, int lineno, char **hostname, int *portnum, enum ast_transport *transport)
{
@@ -708,6 +710,7 @@ int sip_parse_host(char *line, int lineno, char **hostname, int *portnum, enum a
return 0;
}
+#ifdef TEST_FRAMEWORK
AST_TEST_DEFINE(sip_parse_host_line_test)
{
int res = AST_TEST_PASS;
@@ -787,6 +790,7 @@ AST_TEST_DEFINE(sip_parse_host_line_test)
return res;
}
+#endif
/*! \brief Parse the comma-separated nat= option values */
void sip_parse_nat_option(const char *value, struct ast_flags *mask, struct ast_flags *flags)
@@ -834,6 +838,7 @@ void sip_parse_nat_option(const char *value, struct ast_flags *mask, struct ast_
}
}
+#ifdef TEST_FRAMEWORK
#define TEST_FORCE_RPORT 1 << 0
#define TEST_COMEDIA 1 << 1
#define TEST_AUTO_FORCE_RPORT 1 << 2
@@ -904,6 +909,8 @@ AST_TEST_DEFINE(sip_parse_nat_test)
return res;
}
+#endif
+
/*! \brief SIP test registration */
void sip_config_parser_register_tests(void)
{
diff --git a/channels/sip/dialplan_functions.c b/channels/sip/dialplan_functions.c
index 608f45697..e53981d65 100644
--- a/channels/sip/dialplan_functions.c
+++ b/channels/sip/dialplan_functions.c
@@ -23,6 +23,88 @@
<support_level>extended</support_level>
***/
+/*** DOCUMENTATION
+<info name="SIPCHANNEL" language="en_US" tech="SIP">
+ <enumlist>
+ <enum name="peerip">
+ <para>R/O Get the IP address of the peer.</para>
+ </enum>
+ <enum name="recvip">
+ <para>R/O Get the source IP address of the peer.</para>
+ </enum>
+ <enum name="recvport">
+ <para>R/O Get the source port of the peer.</para>
+ </enum>
+ <enum name="from">
+ <para>R/O Get the URI from the From: header.</para>
+ </enum>
+ <enum name="uri">
+ <para>R/O Get the URI from the Contact: header.</para>
+ </enum>
+ <enum name="useragent">
+ <para>R/O Get the useragent.</para>
+ </enum>
+ <enum name="peername">
+ <para>R/O Get the name of the peer.</para>
+ </enum>
+ <enum name="t38passthrough">
+ <para>R/O <literal>1</literal> if T38 is offered or enabled in this channel,
+ otherwise <literal>0</literal></para>
+ </enum>
+ <enum name="rtpqos">
+ <para>R/O Get QOS information about the RTP stream</para>
+ <para> This option takes two additional arguments:</para>
+ <para> Argument 1:</para>
+ <para> <literal>audio</literal> Get data about the audio stream</para>
+ <para> <literal>video</literal> Get data about the video stream</para>
+ <para> <literal>text</literal> Get data about the text stream</para>
+ <para> Argument 2:</para>
+ <para> <literal>local_ssrc</literal> Local SSRC (stream ID)</para>
+ <para> <literal>local_lostpackets</literal> Local lost packets</para>
+ <para> <literal>local_jitter</literal> Local calculated jitter</para>
+ <para> <literal>local_maxjitter</literal> Local calculated jitter (maximum)</para>
+ <para> <literal>local_minjitter</literal> Local calculated jitter (minimum)</para>
+ <para> <literal>local_normdevjitter</literal>Local calculated jitter (normal deviation)</para>
+ <para> <literal>local_stdevjitter</literal> Local calculated jitter (standard deviation)</para>
+ <para> <literal>local_count</literal> Number of received packets</para>
+ <para> <literal>remote_ssrc</literal> Remote SSRC (stream ID)</para>
+ <para> <literal>remote_lostpackets</literal>Remote lost packets</para>
+ <para> <literal>remote_jitter</literal> Remote reported jitter</para>
+ <para> <literal>remote_maxjitter</literal> Remote calculated jitter (maximum)</para>
+ <para> <literal>remote_minjitter</literal> Remote calculated jitter (minimum)</para>
+ <para> <literal>remote_normdevjitter</literal>Remote calculated jitter (normal deviation)</para>
+ <para> <literal>remote_stdevjitter</literal>Remote calculated jitter (standard deviation)</para>
+ <para> <literal>remote_count</literal> Number of transmitted packets</para>
+ <para> <literal>rtt</literal> Round trip time</para>
+ <para> <literal>maxrtt</literal> Round trip time (maximum)</para>
+ <para> <literal>minrtt</literal> Round trip time (minimum)</para>
+ <para> <literal>normdevrtt</literal> Round trip time (normal deviation)</para>
+ <para> <literal>stdevrtt</literal> Round trip time (standard deviation)</para>
+ <para> <literal>all</literal> All statistics (in a form suited to logging,
+ but not for parsing)</para>
+ </enum>
+ <enum name="rtpdest">
+ <para>R/O Get remote RTP destination information.</para>
+ <para> This option takes one additional argument:</para>
+ <para> Argument 1:</para>
+ <para> <literal>audio</literal> Get audio destination</para>
+ <para> <literal>video</literal> Get video destination</para>
+ <para> <literal>text</literal> Get text destination</para>
+ <para> Defaults to <literal>audio</literal> if unspecified.</para>
+ </enum>
+ <enum name="rtpsource">
+ <para>R/O Get source RTP destination information.</para>
+ <para> This option takes one additional argument:</para>
+ <para> Argument 1:</para>
+ <para> <literal>audio</literal> Get audio destination</para>
+ <para> <literal>video</literal> Get video destination</para>
+ <para> <literal>text</literal> Get text destination</para>
+ <para> Defaults to <literal>audio</literal> if unspecified.</para>
+ </enum>
+ </enumlist>
+</info>
+ ***/
+
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
diff --git a/channels/sip/reqresp_parser.c b/channels/sip/reqresp_parser.c
index 31832a33a..ecfb79d0e 100644
--- a/channels/sip/reqresp_parser.c
+++ b/channels/sip/reqresp_parser.c
@@ -258,7 +258,7 @@ int parse_uri_full(char *uri, const char *scheme, char **user, char **pass,
return error;
}
-
+#ifdef TEST_FRAMEWORK
AST_TEST_DEFINE(sip_parse_uri_full_test)
{
int res = AST_TEST_PASS;
@@ -514,7 +514,7 @@ AST_TEST_DEFINE(sip_parse_uri_full_test)
return res;
}
-
+#endif
int parse_uri(char *uri, const char *scheme, char **user, char **pass,
char **hostport, char **transport) {
@@ -530,6 +530,7 @@ int parse_uri(char *uri, const char *scheme, char **user, char **pass,
return ret;
}
+#ifdef TEST_FRAMEWORK
AST_TEST_DEFINE(sip_parse_uri_test)
{
int res = AST_TEST_PASS;
@@ -687,6 +688,7 @@ AST_TEST_DEFINE(sip_parse_uri_test)
return res;
}
+#endif
/*! \brief Get caller id name from SIP headers, copy into output buffer
*
@@ -817,6 +819,7 @@ const char *get_calleridname(const char *input, char *output, size_t outputsize)
return input;
}
+#ifdef TEST_FRAMEWORK
AST_TEST_DEFINE(get_calleridname_test)
{
int res = AST_TEST_PASS;
@@ -899,6 +902,7 @@ AST_TEST_DEFINE(get_calleridname_test)
return res;
}
+#endif
int get_name_and_number(const char *hdr, char **name, char **number)
{
@@ -940,6 +944,7 @@ int get_name_and_number(const char *hdr, char **name, char **number)
return 0;
}
+#ifdef TEST_FRAMEWORK
AST_TEST_DEFINE(get_name_and_number_test)
{
int res = AST_TEST_PASS;
@@ -1044,6 +1049,7 @@ AST_TEST_DEFINE(get_name_and_number_test)
return res;
}
+#endif
int get_in_brackets_const(const char *src,const char **start,int *length)
{
@@ -1176,6 +1182,7 @@ char *get_in_brackets(char *tmp)
return out;
}
+#ifdef TEST_FRAMEWORK
AST_TEST_DEFINE(get_in_brackets_test)
{
int res = AST_TEST_PASS;
@@ -1252,7 +1259,7 @@ AST_TEST_DEFINE(get_in_brackets_test)
return res;
}
-
+#endif
int parse_name_andor_addr(char *uri, const char *scheme, char **name,
char **user, char **pass, char **hostport,
@@ -1298,6 +1305,7 @@ int parse_name_andor_addr(char *uri, const char *scheme, char **name,
return parse_uri_full(uri, scheme, user, pass, hostport, params, headers, residue2);
}
+#ifdef TEST_FRAMEWORK
AST_TEST_DEFINE(parse_name_andor_addr_test)
{
int res = AST_TEST_PASS;
@@ -1427,6 +1435,7 @@ AST_TEST_DEFINE(parse_name_andor_addr_test)
return res;
}
+#endif
int get_comma(char *in, char **out)
{
@@ -1523,6 +1532,7 @@ int parse_contact_header(char *contactheader, struct contactliststruct *contactl
return last;
}
+#ifdef TEST_FRAMEWORK
AST_TEST_DEFINE(parse_contact_header_test)
{
int res = AST_TEST_PASS;
@@ -1668,6 +1678,7 @@ AST_TEST_DEFINE(parse_contact_header_test)
return res;
}
+#endif
/*!
* \brief Parse supported header in incoming packet
@@ -1755,6 +1766,7 @@ unsigned int parse_sip_options(const char *options, char *unsupported, size_t un
return profile;
}
+#ifdef TEST_FRAMEWORK
AST_TEST_DEFINE(sip_parse_options_test)
{
int res = AST_TEST_PASS;
@@ -1892,6 +1904,7 @@ AST_TEST_DEFINE(sip_parse_options_test)
return res;
}
+#endif
/*! \brief helper routine for sip_uri_cmp to compare URI parameters
*
@@ -2246,6 +2259,7 @@ int sip_uri_cmp(const char *input1, const char *input2)
#define URI_CMP_MATCH 0
#define URI_CMP_NOMATCH 1
+#ifdef TEST_FRAMEWORK
AST_TEST_DEFINE(sip_uri_cmp_test)
{
static const struct {
@@ -2362,6 +2376,7 @@ AST_TEST_DEFINE(sip_uri_cmp_test)
return test_res;
}
+#endif
void free_via(struct sip_via *v)
{
@@ -2448,6 +2463,7 @@ struct sip_via *parse_via(const char *header)
return v;
}
+#ifdef TEST_FRAMEWORK
AST_TEST_DEFINE(parse_via_test)
{
int res = AST_TEST_PASS;
@@ -2625,6 +2641,7 @@ AST_TEST_DEFINE(parse_via_test)
}
return res;
}
+#endif
void sip_request_parser_register_tests(void)
{
diff --git a/configs/basic-pbx/modules.conf b/configs/basic-pbx/modules.conf
index 356153713..7b60125b7 100644
--- a/configs/basic-pbx/modules.conf
+++ b/configs/basic-pbx/modules.conf
@@ -60,11 +60,9 @@ 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_pjproject.so
load = res_pjsip_acl.so
@@ -78,7 +76,6 @@ 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
@@ -91,7 +88,6 @@ 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
diff --git a/configs/samples/asterisk.conf.sample b/configs/samples/asterisk.conf.sample
index e4883ec97..b0543d803 100644
--- a/configs/samples/asterisk.conf.sample
+++ b/configs/samples/asterisk.conf.sample
@@ -88,6 +88,14 @@ documentation_language = en_US ; Set the language you want documentation
; considered dangerous because they can allow
; privilege escalation.
; Default no
+;entityid=00:11:22:33:44:55 ; Entity ID.
+ ; This is in the form of a MAC address.
+ ; It should be universally unique.
+ ; It must be unique between servers communicating
+ ; with a protocol that uses this value.
+ ; This is currently is used by DUNDi and
+ ; Exchanging Device and Mailbox State
+ ; using protocols: XMPP, Corosync and PJSIP.
; Changing the following lines may compromise your security.
;[files]
diff --git a/configs/samples/chan_dahdi.conf.sample b/configs/samples/chan_dahdi.conf.sample
index a5daede30..6dd365f47 100644
--- a/configs/samples/chan_dahdi.conf.sample
+++ b/configs/samples/chan_dahdi.conf.sample
@@ -1119,6 +1119,15 @@ pickupgroup=1
;faxdetect=outgoing
;faxdetect=no
;
+; When 'faxdetect' is enabled, one could use 'faxdetect_timeout' to disable fax
+; detection after the specified number of seconds into a call. Be aware that
+; outgoing analog channels may consider the channel is answered immediately
+; when dialing completes. Analog does not have a reliable method of detecting
+; when the far end answers. Zero disables the timeout.
+; Default is 0 to disable the timeout.
+;
+;faxdetect_timeout=30
+;
; When 'faxdetect' is used, one could use 'faxbuffers' to configure the DAHDI
; transmit buffer policy. The default is *OFF*. When this configuration
; option is used, the faxbuffer policy will be used for the life of the call
diff --git a/configs/samples/dsp.conf.sample b/configs/samples/dsp.conf.sample
index 08c5a5715..55951582e 100644
--- a/configs/samples/dsp.conf.sample
+++ b/configs/samples/dsp.conf.sample
@@ -8,28 +8,28 @@
; DTMF Reverse Twist and Normal Twist is the difference in power between the row and column energies.
;
-; Normal Twist is where the Column energy is greater than the Row energy
-; Reverse Twist is where the Row energy is greater.
+; Normal Twist is where the row energy is greater than the column energy.
+; Reverse Twist is where the column energy is greater.
;
; Power level difference between frequencies for different Administrations/RPOAs
-; Power Gain equiv
-; normal reverse dB's
-; AT&T(default) 6.31 2.51 8dB(normal), 4dB(reverse)
-; NTT 3.16 3.16 Max. 5dB
-; Danish 3.98 3.98 Max. 6dB
-; Australian 10.0 10.0 Max. 10dB
-; Brazilian 7.94 7.94 Max. 9dB
-; ETSI 3.98 3.98 Max. 6dB
+; Power Gain equiv
+; normal reverse dB's
+; AT&T(default) 6.31 2.51 8dB(normal), 4dB(reverse)
+; NTT 3.16 3.16 Max. 5dB
+; Danish 3.98 3.98 Max. 6dB
+; Australian 10.0 10.0 Max. 10dB
+; Brazilian 7.94 7.94 Max. 9dB
+; ETSI 3.98 3.98 Max. 6dB
;previous version compatible AT&T values
; RADIO_RELAX disabled, and relaxdtmf=no
-; 6.30 2.50 7.99dB(normal), 3.98dB(reverse)
+; 6.30 2.50 7.99dB(normal), 3.98dB(reverse)
; RADIO_RELAX disabled, and relaxdtmf=yes
-; 6.30 4.00 7.99dB(normal), 6.02dB(reverse)
+; 6.30 4.00 7.99dB(normal), 6.02dB(reverse)
; RADIO_RELAX enabled, and relaxdtmf=no
-; 6.30 2.50 7.99dB(normal), 3.984dB(reverse)
+; 6.30 2.50 7.99dB(normal), 3.984dB(reverse)
; RADIO_RELAX enabled, and relaxdtmf=yes
-; 6.30 6.50 7.99dB(normal), 8.13dB(reverse)
+; 6.30 6.50 7.99dB(normal), 8.13dB(reverse)
;If you don't know what these mean, don't change them.
;dtmf_normal_twist=6.31
diff --git a/configs/samples/hep.conf.sample b/configs/samples/hep.conf.sample
index 6e409d151..e1cd52ebb 100644
--- a/configs/samples/hep.conf.sample
+++ b/configs/samples/hep.conf.sample
@@ -4,7 +4,7 @@
; All settings are currently set in the general section.
[general]
-enabled = yes ; Enable/disable forwarding of packets to a
+enabled = no ; Enable/disable forwarding of packets to a
; HEP server. Default is "yes".
capture_address = 192.168.1.1:9061 ; The address of the HEP capture server.
capture_password = foo ; If specified, the authorization passsword
diff --git a/configs/samples/manager.conf.sample b/configs/samples/manager.conf.sample
index aec2d07f1..0eb64c8ae 100644
--- a/configs/samples/manager.conf.sample
+++ b/configs/samples/manager.conf.sample
@@ -125,7 +125,9 @@ bindaddr = 0.0.0.0
;
; all - All event classes below (including any we may have missed).
; system - General information about the system and ability to run system
-; management commands, such as Shutdown, Restart, and Reload.
+; management commands, such as Shutdown, Restart, and Reload. This
+; class also includes dialplan manipulation actions such as
+; DialplanExtensionAdd and DialplanExtensionRemove.
; call - Information about channels and ability to set information in a
; running channel.
; log - Logging information. Read-only. (Defined but not yet used.)
diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample
index 3e007e48c..2851945ed 100644
--- a/configs/samples/pjsip.conf.sample
+++ b/configs/samples/pjsip.conf.sample
@@ -670,7 +670,7 @@
; usage of media encryption for this endpoint (default:
; "no")
;media_encryption_optimistic=no ; Use encryption if possible but don't fail the call
- ; if not possible.
+ ; 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
@@ -691,6 +691,10 @@
;t38_udptl_maxdatagram=0 ; T 38 UDPTL maximum datagram size (default:
; "0")
;fax_detect=no ; Whether CNG tone detection is enabled (default: "no")
+;fax_detect_timeout=30 ; How many seconds into a call before fax_detect is
+ ; disabled for the call.
+ ; Zero disables the timeout.
+ ; (default: "0")
;t38_udptl_nat=no ; Whether NAT support is enabled on UDPTL sessions
; (default: "no")
;t38_udptl_ipv6=no ; Whether IPv6 is used for UDPTL Sessions (default:
@@ -746,7 +750,7 @@
;srtp_tag_32=no ; Determines whether 32 byte tags should be used instead of 80
; byte tags (default: "no")
;set_var= ; Variable set on a channel involving the endpoint. For multiple
- ; channel variables specify multiple 'set_var'(s)
+ ; 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.
@@ -788,7 +792,7 @@
; (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.
+ ; be used.
; (default: "")
;cert_file= ; Certificate file for endpoint TLS ONLY
; Will read .crt or .pem file but only uses cert,
@@ -874,8 +878,8 @@
;disable_tcp_switch=yes ; Disable automatic switching from UDP to TCP transports
; if outgoing request is too large.
; See RFC 3261 section 18.1.1.
- ; Disabling this option has been known to cause interoperability
- ; issues, so disable at your own risk.
+ ; Disabling this option has been known to cause interoperability
+ ; issues, so disable at your own risk.
; (default: "yes")
;type= ; Must be of type system (default: "")
@@ -905,10 +909,10 @@
;contact_expiration_check_interval=30
; The interval (in seconds) to check for expired contacts.
;disable_multi_domain=no
- ; Disable Multi Domain support.
- ; If disabled it can improve realtime performace by reducing
- ; number of database requsts
- ; (default: "no")
+ ; Disable Multi Domain support.
+ ; If disabled it can improve realtime performace by reducing
+ ; number of database requsts
+ ; (default: "no")
;endpoint_identifier_order=ip,username,anonymous
; The order by which endpoint identifiers are given priority.
; Currently, "ip", "username", "auth_username" and "anonymous" are valid
@@ -952,6 +956,27 @@
; set to this value if there is no better option (such as
; auth/realm) to be used
+ ; Asterisk Task Processor Queue Size
+ ; On heavy loaded system with DB storage you may need to increase
+ ; taskprocessor queue.
+ ; If the taskprocessor queue size reached high water level,
+ ; the alert is triggered.
+ ; If the alert is set the pjsip distibutor stops processing incoming
+ ; requests until the alert is cleared.
+ ; The alert is cleared when taskprocessor queue size drops to the
+ ; low water clear level.
+ ; The next options set taskprocessor queue levels for MWI.
+;mwi_tps_queue_high=500 ; Taskprocessor high water alert trigger level.
+;mwi_tps_queue_low=450 ; Taskprocessor low water clear alert level.
+ ; The default is -1 for 90% of high water level.
+
+ ; Unsolicited MWI
+ ; If there are endpoints configured with unsolicited MWI
+ ; then res_pjsip_mwi module tries to send MWI to all endpoints on startup.
+;mwi_disable_initial_unsolicited=no ; Disable sending unsolicited mwi to all endpoints on startup.
+ ; If disabled then unsolicited mwi will start processing
+ ; on the endpoint's next contact update.
+
; MODULE PROVIDING BELOW SECTION(S): res_pjsip_acl
;==========================ACL SECTION OPTIONS=========================
;[acl]
diff --git a/configs/samples/voicemail.conf.sample b/configs/samples/voicemail.conf.sample
index bb5f2495d..248e142eb 100644
--- a/configs/samples/voicemail.conf.sample
+++ b/configs/samples/voicemail.conf.sample
@@ -376,6 +376,16 @@ sendvoicemail=yes ; Allow the user to compose and send a voicemail while inside
; defaults to being off
; backupdeleted=100
+; Asterisk Task Processor Queue Size
+; On heavy loaded system you may need to increase 'app_voicemail' taskprocessor queue.
+; If the taskprocessor queue size reached high water level, the alert is triggered.
+; If the alert is set then some modules (for example pjsip) slow down its production
+; until the alert is cleared.
+; The alert is cleared when taskprocessor queue size drops to the low water clear level.
+; The next options set taskprocessor queue levels for this module.
+; tps_queue_high=500 ; Taskprocessor high water alert trigger level.
+; tps_queue_low=450 ; Taskprocessor low water clear alert level.
+ ; The default is -1 for 90% of high water level.
[zonemessages]
; Users may be located in different timezones, or may have different
diff --git a/configure b/configure
index ff34770a1..1f6c88869 100755
--- a/configure
+++ b/configure
@@ -915,6 +915,10 @@ PBX_POPT
POPT_DIR
POPT_INCLUDE
POPT_LIB
+PBX_PJSIP_EVSUB_GRP_LOCK
+PJSIP_EVSUB_GRP_LOCK_DIR
+PJSIP_EVSUB_GRP_LOCK_INCLUDE
+PJSIP_EVSUB_GRP_LOCK_LIB
PBX_PJSIP_TLS_TRANSPORT_PROTO
PJSIP_TLS_TRANSPORT_PROTO_DIR
PJSIP_TLS_TRANSPORT_PROTO_INCLUDE
@@ -1112,10 +1116,6 @@ 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
@@ -1175,7 +1175,7 @@ NOISY_BUILD
PTHREAD_CFLAGS
PTHREAD_LIBS
PTHREAD_CC
-acx_pthread_config
+ax_pthread_config
MD5
SOXMIX
PBX_FLEX
@@ -2082,7 +2082,8 @@ Optional Packages:
--with-uriparser=PATH use uriparser library files in PATH
--with-kqueue=PATH use kqueue support files in PATH
--with-ldap=PATH use OpenLDAP files in PATH
- --with-libcurl=DIR look for the curl library in DIR
+ --with-libcurl=PREFIX look for the curl library in PREFIX/lib and headers
+ in PREFIX/include
--with-libedit=PATH use NetBSD Editline library files in PATH, use
'internal' Editline otherwise
--with-libxml2=PATH use LibXML2 files in PATH
@@ -7909,6 +7910,76 @@ if test "${MD5}" = "digest" ; then
MD5="${MD5} -a md5"
fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5
+$as_echo_n "checking for a sed that does not truncate output... " >&6; }
+if ${ac_cv_path_SED+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/
+ for ac_i in 1 2 3 4 5 6 7; do
+ ac_script="$ac_script$as_nl$ac_script"
+ done
+ echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed
+ { ac_script=; unset ac_script;}
+ if test -z "$SED"; then
+ ac_path_SED_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in sed gsed; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_SED="$as_dir/$ac_prog$ac_exec_ext"
+ as_fn_executable_p "$ac_path_SED" || continue
+# Check for GNU ac_path_SED and select it if it is found.
+ # Check for GNU $ac_path_SED
+case `"$ac_path_SED" --version 2>&1` in
+*GNU*)
+ ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo '' >> "conftest.nl"
+ "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_SED_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_SED="$ac_path_SED"
+ ac_path_SED_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_SED_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_SED"; then
+ as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5
+ fi
+else
+ ac_cv_path_SED=$SED
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5
+$as_echo "$ac_cv_path_SED" >&6; }
+ SED="$ac_cv_path_SED"
+ rm -f conftest.sed
+
+
@@ -7918,22 +7989,26 @@ ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
-acx_pthread_ok=no
+ax_pthread_ok=no
# We used to check for pthread.h first, but this fails if pthread.h
-# requires special compiler flags (e.g. on True64 or Sequent).
+# requires special compiler flags (e.g. on Tru64 or Sequent).
# It gets checked for in the link test anyway.
# First of all, check if the user has set any of the PTHREAD_LIBS,
# etcetera environment variables, and if threads linking works using
# them:
-if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
- save_CFLAGS="$CFLAGS"
+if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then
+ ax_pthread_save_CC="$CC"
+ ax_pthread_save_CFLAGS="$CFLAGS"
+ ax_pthread_save_LIBS="$LIBS"
+ if test "x$PTHREAD_CC" != "x"; then :
+ CC="$PTHREAD_CC"
+fi
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
- save_LIBS="$LIBS"
LIBS="$PTHREAD_LIBS $LIBS"
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS" >&5
-$as_echo_n "checking for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS... " >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS" >&5
+$as_echo_n "checking for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -7953,18 +8028,19 @@ return pthread_join ();
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
- acx_pthread_ok=yes
+ ax_pthread_ok=yes
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $acx_pthread_ok" >&5
-$as_echo "$acx_pthread_ok" >&6; }
- if test x"$acx_pthread_ok" = xno; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_ok" >&5
+$as_echo "$ax_pthread_ok" >&6; }
+ if test "x$ax_pthread_ok" = "xno"; then
PTHREAD_LIBS=""
PTHREAD_CFLAGS=""
fi
- LIBS="$save_LIBS"
- CFLAGS="$save_CFLAGS"
+ CC="$ax_pthread_save_CC"
+ CFLAGS="$ax_pthread_save_CFLAGS"
+ LIBS="$ax_pthread_save_LIBS"
fi
# We must check for the threads library under a number of different
@@ -7977,7 +8053,7 @@ fi
# which indicates that we try without any flags at all, and "pthread-config"
# which is a program returning the flags for the Pth emulation library.
-acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
+ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
# The ordering *is* (sometimes) important. Some notes on the
# individual items follow:
@@ -7986,59 +8062,269 @@ acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -m
# none: in case threads are in libc; should be tried before -Kthread and
# other compiler flags to prevent continual compiler warnings
# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
-# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
-# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
-# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
-# -pthreads: Solaris/gcc
-# -mthreads: Mingw32/gcc, Lynx/gcc
+# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64
+# (Note: HP C rejects this with "bad form for `-t' option")
+# -pthreads: Solaris/gcc (Note: HP C also rejects)
# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
-# doesn't hurt to check since this sometimes defines pthreads too;
-# also defines -D_REENTRANT)
-# ... -mt is also the pthreads flag for HP/aCC
+# doesn't hurt to check since this sometimes defines pthreads and
+# -D_REENTRANT too), HP C (must be checked before -lpthread, which
+# is present but should not be used directly; and before -mthreads,
+# because the compiler interprets this as "-mt" + "-hreads")
+# -mthreads: Mingw32/gcc, Lynx/gcc
# pthread: Linux, etcetera
# --thread-safe: KAI C++
# pthread-config: use pthread-config program (for GNU Pth library)
-case "${host_cpu}-${host_os}" in
- *solaris*)
+case $host_os in
+
+ freebsd*)
+
+ # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
+ # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
+
+ ax_pthread_flags="-kthread lthread $ax_pthread_flags"
+ ;;
+
+ hpux*)
+
+ # From the cc(1) man page: "[-mt] Sets various -D flags to enable
+ # multi-threading and also sets -lpthread."
+
+ ax_pthread_flags="-mt -pthread pthread $ax_pthread_flags"
+ ;;
+
+ openedition*)
+
+ # IBM z/OS requires a feature-test macro to be defined in order to
+ # enable POSIX threads at all, so give the user a hint if this is
+ # not set. (We don't define these ourselves, as they can affect
+ # other portions of the system API in unpredictable ways.)
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+# if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS)
+ AX_PTHREAD_ZOS_MISSING
+# endif
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "AX_PTHREAD_ZOS_MISSING" >/dev/null 2>&1; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support." >&5
+$as_echo "$as_me: WARNING: IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support." >&2;}
+fi
+rm -f conftest*
+
+ ;;
+
+ solaris*)
# On Solaris (at least, for some versions), libc contains stubbed
# (non-functional) versions of the pthreads routines, so link-based
- # tests will erroneously succeed. (We need to link with -pthreads/-mt/
- # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
- # a function called by this macro, so we could check for that, but
- # who knows whether they'll stub that too in a future libc.) So,
- # we'll just look for -pthreads and -lpthread first:
+ # tests will erroneously succeed. (N.B.: The stubs are missing
+ # pthread_cleanup_push, or rather a function called by this macro,
+ # so we could check for that, but who knows whether they'll stub
+ # that too in a future libc.) So we'll check first for the
+ # standard Solaris way of linking pthreads (-mt -lpthread).
- acx_pthread_flags="-pthreads pthread -mt -pthread $acx_pthread_flags"
+ ax_pthread_flags="-mt,pthread pthread $ax_pthread_flags"
;;
esac
-if test x"$acx_pthread_ok" = xno; then
-for flag in $acx_pthread_flags; do
+# GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC)
+
+if test "x$GCC" = "xyes"; then :
+ ax_pthread_flags="-pthread -pthreads $ax_pthread_flags"
+fi
+
+# The presence of a feature test macro requesting re-entrant function
+# definitions is, on some systems, a strong hint that pthreads support is
+# correctly enabled
+
+case $host_os in
+ darwin* | hpux* | linux* | osf* | solaris*)
+ ax_pthread_check_macro="_REENTRANT"
+ ;;
- case $flag in
+ aix*)
+ ax_pthread_check_macro="_THREAD_SAFE"
+ ;;
+
+ *)
+ ax_pthread_check_macro="--"
+ ;;
+esac
+if test "x$ax_pthread_check_macro" = "x--"; then :
+ ax_pthread_check_cond=0
+else
+ ax_pthread_check_cond="!defined($ax_pthread_check_macro)"
+fi
+
+# Are we compiling with Clang?
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC is Clang" >&5
+$as_echo_n "checking whether $CC is Clang... " >&6; }
+if ${ax_cv_PTHREAD_CLANG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ax_cv_PTHREAD_CLANG=no
+ # Note that Autoconf sets GCC=yes for Clang as well as GCC
+ if test "x$GCC" = "xyes"; then
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+/* Note: Clang 2.7 lacks __clang_[a-z]+__ */
+# if defined(__clang__) && defined(__llvm__)
+ AX_PTHREAD_CC_IS_CLANG
+# endif
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "AX_PTHREAD_CC_IS_CLANG" >/dev/null 2>&1; then :
+ ax_cv_PTHREAD_CLANG=yes
+fi
+rm -f conftest*
+
+ fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_CLANG" >&5
+$as_echo "$ax_cv_PTHREAD_CLANG" >&6; }
+ax_pthread_clang="$ax_cv_PTHREAD_CLANG"
+
+ax_pthread_clang_warning=no
+
+# Clang needs special handling, because older versions handle the -pthread
+# option in a rather... idiosyncratic way
+
+if test "x$ax_pthread_clang" = "xyes"; then
+
+ # Clang takes -pthread; it has never supported any other flag
+
+ # (Note 1: This will need to be revisited if a system that Clang
+ # supports has POSIX threads in a separate library. This tends not
+ # to be the way of modern systems, but it's conceivable.)
+
+ # (Note 2: On some systems, notably Darwin, -pthread is not needed
+ # to get POSIX threads support; the API is always present and
+ # active. We could reasonably leave PTHREAD_CFLAGS empty. But
+ # -pthread does define _REENTRANT, and while the Darwin headers
+ # ignore this macro, third-party headers might not.)
+
+ PTHREAD_CFLAGS="-pthread"
+ PTHREAD_LIBS=
+
+ ax_pthread_ok=yes
+
+ # However, older versions of Clang make a point of warning the user
+ # that, in an invocation where only linking and no compilation is
+ # taking place, the -pthread option has no effect ("argument unused
+ # during compilation"). They expect -pthread to be passed in only
+ # when source code is being compiled.
+ #
+ # Problem is, this is at odds with the way Automake and most other
+ # C build frameworks function, which is that the same flags used in
+ # compilation (CFLAGS) are also used in linking. Many systems
+ # supported by AX_PTHREAD require exactly this for POSIX threads
+ # support, and in fact it is often not straightforward to specify a
+ # flag that is used only in the compilation phase and not in
+ # linking. Such a scenario is extremely rare in practice.
+ #
+ # Even though use of the -pthread flag in linking would only print
+ # a warning, this can be a nuisance for well-run software projects
+ # that build with -Werror. So if the active version of Clang has
+ # this misfeature, we search for an option to squash it.
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether Clang needs flag to prevent \"argument unused\" warning when linking with -pthread" >&5
+$as_echo_n "checking whether Clang needs flag to prevent \"argument unused\" warning when linking with -pthread... " >&6; }
+if ${ax_cv_PTHREAD_CLANG_NO_WARN_FLAG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown
+ # Create an alternate version of $ac_link that compiles and
+ # links in two steps (.c -> .o, .o -> exe) instead of one
+ # (.c -> exe), because the warning occurs only in the second
+ # step
+ ax_pthread_save_ac_link="$ac_link"
+ ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g'
+ ax_pthread_link_step=`$as_echo "$ac_link" | sed "$ax_pthread_sed"`
+ ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)"
+ ax_pthread_save_CFLAGS="$CFLAGS"
+ for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do
+ if test "x$ax_pthread_try" = "xunknown"; then :
+ break
+fi
+ CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS"
+ ac_link="$ax_pthread_save_ac_link"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+int main(void){return 0;}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_link="$ax_pthread_2step_ac_link"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+int main(void){return 0;}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ break
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ done
+ ac_link="$ax_pthread_save_ac_link"
+ CFLAGS="$ax_pthread_save_CFLAGS"
+ if test "x$ax_pthread_try" = "x"; then :
+ ax_pthread_try=no
+fi
+ ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" >&5
+$as_echo "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" >&6; }
+
+ case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in
+ no | unknown) ;;
+ *) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;;
+ esac
+
+fi # $ax_pthread_clang = yes
+
+if test "x$ax_pthread_ok" = "xno"; then
+for ax_pthread_try_flag in $ax_pthread_flags; do
+
+ case $ax_pthread_try_flag in
none)
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads work without any flags" >&5
$as_echo_n "checking whether pthreads work without any flags... " >&6; }
;;
+ -mt,pthread)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads work with -mt -lpthread" >&5
+$as_echo_n "checking whether pthreads work with -mt -lpthread... " >&6; }
+ PTHREAD_CFLAGS="-mt"
+ PTHREAD_LIBS="-lpthread"
+ ;;
+
-*)
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads work with $flag" >&5
-$as_echo_n "checking whether pthreads work with $flag... " >&6; }
- PTHREAD_CFLAGS="$flag"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads work with $ax_pthread_try_flag" >&5
+$as_echo_n "checking whether pthreads work with $ax_pthread_try_flag... " >&6; }
+ PTHREAD_CFLAGS="$ax_pthread_try_flag"
;;
- pthread-config)
- # Extract the first word of "pthread-config", so it can be a program name with args.
+ pthread-config)
+ # Extract the first word of "pthread-config", so it can be a program name with args.
set dummy pthread-config; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_acx_pthread_config+:} false; then :
+if ${ac_cv_prog_ax_pthread_config+:} false; then :
$as_echo_n "(cached) " >&6
else
- if test -n "$acx_pthread_config"; then
- ac_cv_prog_acx_pthread_config="$acx_pthread_config" # Let the user override the test.
+ if test -n "$ax_pthread_config"; then
+ ac_cv_prog_ax_pthread_config="$ax_pthread_config" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
@@ -8047,7 +8333,7 @@ do
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
- ac_cv_prog_acx_pthread_config="yes"
+ ac_cv_prog_ax_pthread_config="yes"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
@@ -8055,35 +8341,37 @@ done
done
IFS=$as_save_IFS
- test -z "$ac_cv_prog_acx_pthread_config" && ac_cv_prog_acx_pthread_config="no"
+ test -z "$ac_cv_prog_ax_pthread_config" && ac_cv_prog_ax_pthread_config="no"
fi
fi
-acx_pthread_config=$ac_cv_prog_acx_pthread_config
-if test -n "$acx_pthread_config"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $acx_pthread_config" >&5
-$as_echo "$acx_pthread_config" >&6; }
+ax_pthread_config=$ac_cv_prog_ax_pthread_config
+if test -n "$ax_pthread_config"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_config" >&5
+$as_echo "$ax_pthread_config" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
- if test x"$acx_pthread_config" = xno; then continue; fi
- PTHREAD_CFLAGS="`pthread-config --cflags`"
- PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
- ;;
+ if test "x$ax_pthread_config" = "xno"; then :
+ continue
+fi
+ PTHREAD_CFLAGS="`pthread-config --cflags`"
+ PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
+ ;;
*)
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for the pthreads library -l$flag" >&5
-$as_echo_n "checking for the pthreads library -l$flag... " >&6; }
- PTHREAD_LIBS="-l$flag"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for the pthreads library -l$ax_pthread_try_flag" >&5
+$as_echo_n "checking for the pthreads library -l$ax_pthread_try_flag... " >&6; }
+ PTHREAD_LIBS="-l$ax_pthread_try_flag"
;;
esac
- save_LIBS="$LIBS"
- save_CFLAGS="$CFLAGS"
- LIBS="$PTHREAD_LIBS $LIBS"
+ ax_pthread_save_CFLAGS="$CFLAGS"
+ ax_pthread_save_LIBS="$LIBS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+ LIBS="$PTHREAD_LIBS $LIBS"
# Check for various functions. We must include pthread.h,
# since some functions may be macros. (On the Sequent, we
@@ -8094,33 +8382,42 @@ $as_echo_n "checking for the pthreads library -l$flag... " >&6; }
# pthread_cleanup_push because it is one of the few pthread
# functions on Solaris that doesn't have a non-functional libc stub.
# We try pthread_create on general principles.
+
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <pthread.h>
+# if $ax_pthread_check_cond
+# error "$ax_pthread_check_macro must be defined"
+# endif
+ static void routine(void *a) { a = 0; }
+ static void *start_routine(void *a) { return a; }
int
main ()
{
-pthread_t th; pthread_join(th, 0);
- pthread_attr_init(0); pthread_cleanup_push(0, 0);
- pthread_create(0,0,0,0); pthread_cleanup_pop(0);
+pthread_t th; pthread_attr_t attr;
+ pthread_create(&th, 0, start_routine, 0);
+ pthread_join(th, 0);
+ pthread_attr_init(&attr);
+ pthread_cleanup_push(routine, 0);
+ pthread_cleanup_pop(0) /* ; */
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
- acx_pthread_ok=yes
+ ax_pthread_ok=yes
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
- LIBS="$save_LIBS"
- CFLAGS="$save_CFLAGS"
+ CFLAGS="$ax_pthread_save_CFLAGS"
+ LIBS="$ax_pthread_save_LIBS"
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $acx_pthread_ok" >&5
-$as_echo "$acx_pthread_ok" >&6; }
- if test "x$acx_pthread_ok" = xyes; then
- break;
- fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_ok" >&5
+$as_echo "$ax_pthread_ok" >&6; }
+ if test "x$ax_pthread_ok" = "xyes"; then :
+ break
+fi
PTHREAD_LIBS=""
PTHREAD_CFLAGS=""
@@ -8128,63 +8425,127 @@ done
fi
# Various other checks:
-if test "x$acx_pthread_ok" = xyes; then
- save_LIBS="$LIBS"
- LIBS="$PTHREAD_LIBS $LIBS"
- save_CFLAGS="$CFLAGS"
+if test "x$ax_pthread_ok" = "xyes"; then
+ ax_pthread_save_CFLAGS="$CFLAGS"
+ ax_pthread_save_LIBS="$LIBS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+ LIBS="$PTHREAD_LIBS $LIBS"
# Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for joinable pthread attribute" >&5
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for joinable pthread attribute" >&5
$as_echo_n "checking for joinable pthread attribute... " >&6; }
- attr_name=unknown
- for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+if ${ax_cv_PTHREAD_JOINABLE_ATTR+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ax_cv_PTHREAD_JOINABLE_ATTR=unknown
+ for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <pthread.h>
int
main ()
{
-int attr=$attr; return attr;
+int attr = $ax_pthread_attr; return attr /* ; */
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
- attr_name=$attr; break
+ ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
- done
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $attr_name" >&5
-$as_echo "$attr_name" >&6; }
- if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
+ done
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_JOINABLE_ATTR" >&5
+$as_echo "$ax_cv_PTHREAD_JOINABLE_ATTR" >&6; }
+ if test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \
+ test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \
+ test "x$ax_pthread_joinable_attr_defined" != "xyes"; then :
cat >>confdefs.h <<_ACEOF
-#define PTHREAD_CREATE_JOINABLE $attr_name
+#define PTHREAD_CREATE_JOINABLE $ax_cv_PTHREAD_JOINABLE_ATTR
_ACEOF
- fi
+ ax_pthread_joinable_attr_defined=yes
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking if more special flags are required for pthreads" >&5
-$as_echo_n "checking if more special flags are required for pthreads... " >&6; }
- flag=no
- case "${host_cpu}-${host_os}" in
- *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";;
- *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";;
- esac
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${flag}" >&5
-$as_echo "${flag}" >&6; }
- if test "x$flag" != xno; then
- PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
- fi
+fi
- LIBS="$save_LIBS"
- CFLAGS="$save_CFLAGS"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether more special flags are required for pthreads" >&5
+$as_echo_n "checking whether more special flags are required for pthreads... " >&6; }
+if ${ax_cv_PTHREAD_SPECIAL_FLAGS+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ax_cv_PTHREAD_SPECIAL_FLAGS=no
+ case $host_os in
+ solaris*)
+ ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS"
+ ;;
+ esac
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_SPECIAL_FLAGS" >&5
+$as_echo "$ax_cv_PTHREAD_SPECIAL_FLAGS" >&6; }
+ if test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \
+ test "x$ax_pthread_special_flags_added" != "xyes"; then :
+ PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS"
+ ax_pthread_special_flags_added=yes
+fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for PTHREAD_PRIO_INHERIT" >&5
+$as_echo_n "checking for PTHREAD_PRIO_INHERIT... " >&6; }
+if ${ax_cv_PTHREAD_PRIO_INHERIT+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <pthread.h>
+int
+main ()
+{
+int i = PTHREAD_PRIO_INHERIT;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ax_cv_PTHREAD_PRIO_INHERIT=yes
+else
+ ax_cv_PTHREAD_PRIO_INHERIT=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_PRIO_INHERIT" >&5
+$as_echo "$ax_cv_PTHREAD_PRIO_INHERIT" >&6; }
+ if test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \
+ test "x$ax_pthread_prio_inherit_defined" != "xyes"; then :
- # More AIX lossage: must compile with xlc_r or cc_r
- if test x"$GCC" != xyes; then
- for ac_prog in xlc_r cc_r
+$as_echo "#define HAVE_PTHREAD_PRIO_INHERIT 1" >>confdefs.h
+
+ ax_pthread_prio_inherit_defined=yes
+
+fi
+
+ CFLAGS="$ax_pthread_save_CFLAGS"
+ LIBS="$ax_pthread_save_LIBS"
+
+ # More AIX lossage: compile with *_r variant
+ if test "x$GCC" != "xyes"; then
+ case $host_os in
+ aix*)
+ case "x/$CC" in #(
+ x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6) :
+ #handle absolute path differently from PATH based program lookup
+ case "x$CC" in #(
+ x/*) :
+ if as_fn_executable_p ${CC}_r; then :
+ PTHREAD_CC="${CC}_r"
+fi ;; #(
+ *) :
+ for ac_prog in ${CC}_r
do
# Extract the first word of "$ac_prog", so it can be a program name with args.
set dummy $ac_prog; ac_word=$2
@@ -8225,27 +8586,31 @@ fi
test -n "$PTHREAD_CC" && break
done
-test -n "$PTHREAD_CC" || PTHREAD_CC="${CC}"
-
- else
- PTHREAD_CC=$CC
- fi
-else
- PTHREAD_CC="$CC"
+test -n "$PTHREAD_CC" || PTHREAD_CC="$CC"
+ ;;
+esac ;; #(
+ *) :
+ ;;
+esac
+ ;;
+ esac
+ fi
fi
+test -n "$PTHREAD_CC" || PTHREAD_CC="$CC"
+
# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
-if test x"$acx_pthread_ok" = xyes; then
+if test "x$ax_pthread_ok" = "xyes"; then
$as_echo "#define HAVE_PTHREAD 1" >>confdefs.h
:
else
- acx_pthread_ok=no
+ ax_pthread_ok=no
fi
ac_ext=c
@@ -8774,18 +9139,6 @@ 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
@@ -9447,6 +9800,10 @@ fi
+
+
+
+
# Check whether --with-libcurl was given.
if test "${with_libcurl+set}" = set; then :
withval=$with_libcurl; _libcurl_with=$withval
@@ -9534,7 +9891,6 @@ done
done
IFS=$as_save_IFS
- test -z "$ac_cv_path__libcurl_config" && ac_cv_path__libcurl_config=""$withval/bin""
;;
esac
fi
@@ -9684,10 +10040,11 @@ int x;
curl_easy_setopt(NULL,CURLOPT_URL,NULL);
x=CURL_ERROR_SIZE;
x=CURLOPT_WRITEFUNCTION;
-x=CURLOPT_FILE;
+x=CURLOPT_WRITEDATA;
x=CURLOPT_ERRORBUFFER;
x=CURLOPT_STDERR;
x=CURLOPT_VERBOSE;
+if (x) {;}
;
return 0;
@@ -9754,17 +10111,23 @@ _ACEOF
# We don't have --protocols, so just assume that all
# protocols are available
- _libcurl_protocols="HTTP FTP FILE TELNET LDAP DICT"
+ _libcurl_protocols="HTTP FTP FILE TELNET LDAP DICT TFTP"
if test x$libcurl_feature_SSL = xyes ; then
_libcurl_protocols="$_libcurl_protocols HTTPS"
# FTPS wasn't standards-compliant until version
- # 7.11.0
+ # 7.11.0 (0x070b00 == 461568)
if test $_libcurl_version -ge 461568; then
_libcurl_protocols="$_libcurl_protocols FTPS"
fi
fi
+
+ # RTSP, IMAP, POP3 and SMTP were added in
+ # 7.20.0 (0x071400 == 463872)
+ if test $_libcurl_version -ge 463872; then
+ _libcurl_protocols="$_libcurl_protocols RTSP IMAP POP3 SMTP"
+ fi
fi
for _libcurl_protocol in $_libcurl_protocols ; do
@@ -10563,6 +10926,18 @@ PBX_PJSIP_TLS_TRANSPORT_PROTO=0
+PJSIP_EVSUB_GRP_LOCK_DESCRIP="PJSIP EVSUB Group Lock support"
+PJSIP_EVSUB_GRP_LOCK_OPTION=pjsip
+PJSIP_EVSUB_GRP_LOCK_DIR=${PJPROJECT_DIR}
+
+PBX_PJSIP_EVSUB_GRP_LOCK=0
+
+
+
+
+
+
+
POPT_DESCRIP="popt"
POPT_OPTION="popt"
@@ -13612,7 +13987,7 @@ else
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -13658,7 +14033,7 @@ else
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -13682,7 +14057,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -13727,7 +14102,7 @@ else
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -13751,7 +14126,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -15148,46 +15523,6 @@ _ACEOF
rm -f conftest*
-if ${ac_cv_func_setvbuf_reversed+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- ac_cv_func_setvbuf_reversed=no
-fi
-
-
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking return type of signal handlers" >&5
-$as_echo_n "checking return type of signal handlers... " >&6; }
-if ${ac_cv_type_signal+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-#include <sys/types.h>
-#include <signal.h>
-
-int
-main ()
-{
-return *(signal (0, 0)) (0) == 1;
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
- ac_cv_type_signal=int
-else
- ac_cv_type_signal=void
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_signal" >&5
-$as_echo "$ac_cv_type_signal" >&6; }
-
-cat >>confdefs.h <<_ACEOF
-#define RETSIGTYPE $ac_cv_type_signal
-_ACEOF
-
-
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether lstat correctly handles trailing slash" >&5
$as_echo_n "checking whether lstat correctly handles trailing slash... " >&6; }
if ${ac_cv_func_lstat_dereferences_slashed_symlink+:} false; then :
@@ -17528,6 +17863,74 @@ CFLAGS="$saved_CFLAGS"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for compiler 'attribute noreturn' support" >&5
+$as_echo_n "checking for compiler 'attribute noreturn' support... " >&6; }
+saved_CFLAGS="$CFLAGS"
+CFLAGS="$CFLAGS -Wall -Wno-unused -Werror"
+
+
+if test "xnoreturn" = "x"
+then
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+ void __attribute__((noreturn)) *test(void *muffin, ...) {return (void *) 0;}
+int
+main ()
+{
+
+ ;
+ 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; }
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ATTRIBUTE_noreturn 1
+_ACEOF
+
+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
+else
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+ void __attribute__((noreturn)) *test(void *muffin, ...) ;
+int
+main ()
+{
+
+ ;
+ 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; }
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ATTRIBUTE_noreturn 1
+_ACEOF
+
+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
+fi
+
+
+CFLAGS="$saved_CFLAGS"
+
+
+
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for -fsanitize=address support" >&5
$as_echo_n "checking for -fsanitize=address support... " >&6; }
saved_sanitize_CFLAGS="${CFLAGS}"
@@ -17780,19 +18183,13 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for -march=native support" >&5
$as_echo_n "checking for -march=native support... " >&6; }
if $(${CC} -march=native -S -o /dev/null -xc /dev/null > /dev/null 2>&1); then
- if test "${CONFIG_CFLAGS}" = ""; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
- AST_NATIVE_ARCH=1
- else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: user CFLAGS present" >&5
-$as_echo "user CFLAGS present" >&6; }
- AST_NATIVE_ARCH=
- fi
+ AST_NATIVE_ARCH=1
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
- AST_NATIVE_ARCH=
+ AST_NATIVE_ARCH=0
fi
@@ -24463,6 +24860,9 @@ rm -f conftest*
$as_echo "#define HAVE_PJSIP_TLS_TRANSPORT_PROTO 1" >>confdefs.h
+$as_echo "#define HAVE_PJSIP_EVSUB_GRP_LOCK 1" >>confdefs.h
+
+
else
if test "x${PBX_PJPROJECT}" != "x1" -a "${USE_PJPROJECT}" != "no"; then
@@ -25178,6 +25578,111 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
LIBS="${saved_libs}"
CPPFLAGS="${saved_cppflags}"
+
+
+if test "x${PBX_PJSIP_EVSUB_GRP_LOCK}" != "x1" -a "${USE_PJSIP_EVSUB_GRP_LOCK}" != "no"; then
+ pbxlibdir=""
+ # if --with-PJSIP_EVSUB_GRP_LOCK=DIR has been specified, use it.
+ if test "x${PJSIP_EVSUB_GRP_LOCK_DIR}" != "x"; then
+ if test -d ${PJSIP_EVSUB_GRP_LOCK_DIR}/lib; then
+ pbxlibdir="-L${PJSIP_EVSUB_GRP_LOCK_DIR}/lib"
+ else
+ pbxlibdir="-L${PJSIP_EVSUB_GRP_LOCK_DIR}"
+ fi
+ fi
+ pbxfuncname="pjsip_evsub_add_ref"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_PJSIP_EVSUB_GRP_LOCK_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_LIB $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_EVSUB_GRP_LOCK_FOUND=yes
+else
+ AST_PJSIP_EVSUB_GRP_LOCK_FOUND=no
+fi
+
+ CFLAGS="${ast_ext_lib_check_save_CFLAGS}"
+ fi
+
+ # now check for the header.
+ if test "${AST_PJSIP_EVSUB_GRP_LOCK_FOUND}" = "yes"; then
+ PJSIP_EVSUB_GRP_LOCK_LIB="${pbxlibdir} -lpjsip $PJPROJECT_LIB"
+ # if --with-PJSIP_EVSUB_GRP_LOCK=DIR has been specified, use it.
+ if test "x${PJSIP_EVSUB_GRP_LOCK_DIR}" != "x"; then
+ PJSIP_EVSUB_GRP_LOCK_INCLUDE="-I${PJSIP_EVSUB_GRP_LOCK_DIR}/include"
+ fi
+ PJSIP_EVSUB_GRP_LOCK_INCLUDE="${PJSIP_EVSUB_GRP_LOCK_INCLUDE} $PJPROJECT_CFLAGS"
+ if test "xpjsip.h" = "x" ; then # no header, assume found
+ PJSIP_EVSUB_GRP_LOCK_HEADER_FOUND="1"
+ else # check for the header
+ ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${PJSIP_EVSUB_GRP_LOCK_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_EVSUB_GRP_LOCK_HEADER_FOUND=1
+else
+ PJSIP_EVSUB_GRP_LOCK_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${ast_ext_lib_check_saved_CPPFLAGS}"
+ fi
+ if test "x${PJSIP_EVSUB_GRP_LOCK_HEADER_FOUND}" = "x0" ; then
+ PJSIP_EVSUB_GRP_LOCK_LIB=""
+ PJSIP_EVSUB_GRP_LOCK_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ PJSIP_EVSUB_GRP_LOCK_LIB=""
+ fi
+ PBX_PJSIP_EVSUB_GRP_LOCK=1
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_PJSIP_EVSUB_GRP_LOCK 1
+_ACEOF
+
+ fi
+ fi
+fi
+
+
fi
fi
@@ -31141,53 +31646,6 @@ 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; }
diff --git a/configure.ac b/configure.ac
index f2e42ba1f..637059cd4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -352,7 +352,7 @@ if test "${MD5}" = "digest" ; then
MD5="${MD5} -a md5"
fi
-ACX_PTHREAD
+AX_PTHREAD
AC_LANG(C)
@@ -415,7 +415,6 @@ 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])
@@ -487,6 +486,7 @@ AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_GET_DEST_INFO], [pjsip_get_dest_info support],
AST_EXT_LIB_SETUP_OPTIONAL([PJ_SSL_CERT_LOAD_FROM_FILES2], [pj_ssl_cert_load_from_files2 support], [PJPROJECT], [pjsip])
AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_EXTERNAL_RESOLVER], [PJSIP External Resolver Support], [PJPROJECT], [pjsip])
AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_TLS_TRANSPORT_PROTO], [PJSIP TLS Transport proto field support], [PJPROJECT], [pjsip])
+AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_EVSUB_GRP_LOCK], [PJSIP EVSUB Group Lock support], [PJPROJECT], [pjsip])
AST_EXT_LIB_SETUP([POPT], [popt], [popt])
AST_EXT_LIB_SETUP([PORTAUDIO], [PortAudio], [portaudio])
@@ -682,8 +682,6 @@ AC_PROG_GCC_TRADITIONAL
AC_FUNC_MEMCMP
AC_FUNC_MMAP
AC_FUNC_SELECT_ARGTYPES
-AC_FUNC_SETVBUF_REVERSED
-AC_TYPE_SIGNAL
AC_FUNC_STAT
AC_FUNC_STRCOLL
AC_FUNC_STRFTIME
@@ -1074,6 +1072,7 @@ AST_GCC_ATTRIBUTE(warn_unused_result)
AST_GCC_ATTRIBUTE(may_alias)
AST_GCC_ATTRIBUTE(constructor)
AST_GCC_ATTRIBUTE(destructor)
+AST_GCC_ATTRIBUTE(noreturn,noreturn)
AC_MSG_CHECKING(for -fsanitize=address support)
saved_sanitize_CFLAGS="${CFLAGS}"
@@ -1214,23 +1213,18 @@ AC_SUBST(AST_SHADOW_WARNINGS)
AC_MSG_CHECKING(for -march=native support)
if $(${CC} -march=native -S -o /dev/null -xc /dev/null > /dev/null 2>&1); then
- if test "${CONFIG_CFLAGS}" = ""; then
- AC_MSG_RESULT(yes)
- AST_NATIVE_ARCH=1
- else
- AC_MSG_RESULT(user CFLAGS present)
- AST_NATIVE_ARCH=
- fi
+ AC_MSG_RESULT(yes)
+ AST_NATIVE_ARCH=1
else
AC_MSG_RESULT(no)
- AST_NATIVE_ARCH=
+ AST_NATIVE_ARCH=0
fi
AC_SUBST(AST_NATIVE_ARCH)
dnl Check to see if rpath should be set in LDFLAGS
AC_ARG_ENABLE(rpath,
- [AC_HELP_STRING([--disable-rpath],
+ [AS_HELP_STRING([--disable-rpath],
[Disables rpath linker option checking])],
[case "${enableval}" in
y|ye|yes) check_rpath=yes ;;
@@ -2194,6 +2188,8 @@ if test "$USE_PJPROJECT" != "no" ; then
AST_C_COMPILE_CHECK([PJSIP_TLS_TRANSPORT_PROTO], [struct pjsip_tls_setting setting; int proto; proto = setting.proto;], [pjsip.h])
LIBS="${saved_libs}"
CPPFLAGS="${saved_cppflags}"
+
+ AST_EXT_LIB_CHECK([PJSIP_EVSUB_GRP_LOCK], [pjsip], [pjsip_evsub_add_ref], [pjsip.h], [$PJPROJECT_LIB], [$PJPROJECT_CFLAGS])
fi
fi
@@ -2397,11 +2393,6 @@ 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
diff --git a/contrib/ast-db-manage/config/versions/3772f8f828da_update_identify_by.py b/contrib/ast-db-manage/config/versions/3772f8f828da_update_identify_by.py
new file mode 100644
index 000000000..92695b09f
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/3772f8f828da_update_identify_by.py
@@ -0,0 +1,44 @@
+"""update_identify_by
+
+Revision ID: 3772f8f828da
+Revises: c7a44a5a0851
+Create Date: 2016-08-11 10:47:29.211063
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '3772f8f828da'
+down_revision = 'c7a44a5a0851'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def enum_update(table_name, column_name, enum_name, enum_values):
+ if op.get_context().bind.dialect.name != 'postgresql':
+ op.alter_column(table_name, column_name,
+ type_=sa.Enum(*enum_values, name=enum_name))
+ return
+
+ # Postgres requires a few more steps
+ tmp = enum_name + '_tmp'
+
+ op.execute('ALTER TYPE ' + enum_name + ' RENAME TO ' + tmp)
+
+ updated = sa.Enum(*enum_values, name=enum_name)
+ updated.create(op.get_bind(), checkfirst=False)
+
+ op.execute('ALTER TABLE ' + table_name + ' ALTER COLUMN ' + column_name +
+ ' TYPE ' + enum_name + ' USING identify_by::text::' + enum_name)
+
+ op.execute('DROP TYPE ' + tmp)
+
+
+def upgrade():
+ enum_update('ps_endpoints', 'identify_by', 'pjsip_identify_by_values',
+ ['username', 'auth_username'])
+
+
+def downgrade():
+ enum_update('ps_endpoints', 'identify_by', 'pjsip_identify_by_values',
+ ['username'])
diff --git a/contrib/ast-db-manage/config/versions/4a6c67fa9b7a_add_fax_detect_timeout_option.py b/contrib/ast-db-manage/config/versions/4a6c67fa9b7a_add_fax_detect_timeout_option.py
new file mode 100644
index 000000000..91774c447
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/4a6c67fa9b7a_add_fax_detect_timeout_option.py
@@ -0,0 +1,23 @@
+"""add fax_detect_timeout option
+
+Revision ID: 4a6c67fa9b7a
+Revises: 9deac0ae4717
+Create Date: 2016-07-18 18:20:44.249491
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '4a6c67fa9b7a'
+down_revision = '9deac0ae4717'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+ op.add_column('ps_endpoints', sa.Column('fax_detect_timeout', sa.Integer))
+
+
+def downgrade():
+ op.drop_column('ps_endpoints', 'fax_detect_timeout')
+
diff --git a/contrib/ast-db-manage/config/versions/581a4264e537_adding_extensions.py b/contrib/ast-db-manage/config/versions/581a4264e537_adding_extensions.py
index ecee0e04e..415f5bc25 100755
--- a/contrib/ast-db-manage/config/versions/581a4264e537_adding_extensions.py
+++ b/contrib/ast-db-manage/config/versions/581a4264e537_adding_extensions.py
@@ -31,20 +31,18 @@ down_revision = '43956d550a44'
from alembic import op
import sqlalchemy as sa
-
def upgrade():
op.create_table(
'extensions',
sa.Column('id', sa.BigInteger, primary_key=True, nullable=False,
unique=True, autoincrement=True),
- sa.Column('context', sa.String(40), primary_key=True, nullable=False),
- sa.Column('exten', sa.String(40), primary_key=True, nullable=False),
- sa.Column('priority', sa.Integer, primary_key=True, nullable=False,
- autoincrement=True),
+ sa.Column('context', sa.String(40), nullable=False),
+ sa.Column('exten', sa.String(40), nullable=False),
+ sa.Column('priority', sa.Integer, nullable=False),
sa.Column('app', sa.String(40), nullable=False),
sa.Column('appdata', sa.String(256), nullable=False),
+ sa.UniqueConstraint('context', 'exten', 'priority')
)
-
def downgrade():
op.drop_table('extensions')
diff --git a/contrib/ast-db-manage/config/versions/81b01a191a46_pjsip_add_contact_reg_server.py b/contrib/ast-db-manage/config/versions/81b01a191a46_pjsip_add_contact_reg_server.py
index 0318b9a4f..0919370ba 100644
--- a/contrib/ast-db-manage/config/versions/81b01a191a46_pjsip_add_contact_reg_server.py
+++ b/contrib/ast-db-manage/config/versions/81b01a191a46_pjsip_add_contact_reg_server.py
@@ -16,10 +16,8 @@ import sqlalchemy as sa
def upgrade():
op.add_column('ps_contacts', sa.Column('reg_server', sa.String(20)))
- op.drop_constraint('id', 'ps_contacts', type_='unique')
op.create_unique_constraint('ps_contacts_uq', 'ps_contacts', ['id','reg_server'])
def downgrade():
op.drop_constraint('ps_contacts_uq', 'ps_contacts', type_='unique')
op.drop_column('ps_contacts', 'reg_server')
- op.create_unique_constraint(None, 'ps_contacts', 'id')
diff --git a/contrib/ast-db-manage/config/versions/9deac0ae4717_pjsip_add_subscribe_context.py b/contrib/ast-db-manage/config/versions/9deac0ae4717_pjsip_add_subscribe_context.py
new file mode 100644
index 000000000..2358fdd59
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/9deac0ae4717_pjsip_add_subscribe_context.py
@@ -0,0 +1,21 @@
+"""pjsip_add_subscribe_context
+
+Revision ID: 9deac0ae4717
+Revises: ef7efc2d3964
+Create Date: 2016-07-04 12:11:28.117788
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '9deac0ae4717'
+down_revision = 'ef7efc2d3964'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+ op.add_column('ps_endpoints', sa.Column('subscribe_context', sa.String(40)))
+
+def downgrade():
+ op.drop_column('ps_endpoints', 'subscribe_context')
diff --git a/contrib/ast-db-manage/config/versions/c7a44a5a0851_pjsip_add_global_mwi_options.py b/contrib/ast-db-manage/config/versions/c7a44a5a0851_pjsip_add_global_mwi_options.py
new file mode 100644
index 000000000..d3efa2278
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/c7a44a5a0851_pjsip_add_global_mwi_options.py
@@ -0,0 +1,35 @@
+"""pjsip: add global MWI options
+
+Revision ID: c7a44a5a0851
+Revises: 4a6c67fa9b7a
+Create Date: 2016-08-03 15:08:22.524727
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = 'c7a44a5a0851'
+down_revision = '4a6c67fa9b7a'
+
+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_globals', sa.Column('mwi_tps_queue_high', sa.Integer))
+ op.add_column('ps_globals', sa.Column('mwi_tps_queue_low', sa.Integer))
+ op.add_column('ps_globals', sa.Column('mwi_disable_initial_unsolicited', yesno_values))
+
+def downgrade():
+ op.drop_column('ps_globals', 'mwi_tps_queue_high')
+ op.drop_column('ps_globals', 'mwi_tps_queue_low')
+ op.drop_column('ps_globals', 'mwi_disable_initial_unsolicited')
diff --git a/contrib/ast-db-manage/config/versions/ef7efc2d3964_ps_contacts_add_endpoint_and_modify_.py b/contrib/ast-db-manage/config/versions/ef7efc2d3964_ps_contacts_add_endpoint_and_modify_.py
new file mode 100644
index 000000000..43d4028f2
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/ef7efc2d3964_ps_contacts_add_endpoint_and_modify_.py
@@ -0,0 +1,33 @@
+"""ps_contacts add endpoint and modify expiration_time to bigint
+
+Revision ID: ef7efc2d3964
+Revises: a845e4d8ade8
+Create Date: 2016-06-02 18:18:46.231920
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = 'ef7efc2d3964'
+down_revision = 'a845e4d8ade8'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+ context = op.get_context()
+
+ op.add_column('ps_contacts', sa.Column('endpoint', sa.String(40)))
+
+ if context.bind.dialect.name != 'postgresql':
+ op.alter_column('ps_contacts', 'expiration_time', type_=sa.BigInteger)
+ else:
+ op.execute('ALTER TABLE ps_contacts ALTER COLUMN expiration_time TYPE BIGINT USING expiration_time::bigint')
+
+ op.create_index('ps_contacts_qualifyfreq_exp', 'ps_contacts', ['qualify_frequency', 'expiration_time'])
+ op.create_index('ps_aors_qualifyfreq_contact', 'ps_aors', ['qualify_frequency', 'contact'])
+def downgrade():
+ op.drop_index('ps_aors_qualifyfreq_contact')
+ op.drop_index('ps_contacts_qualifyfreq_exp')
+ op.drop_column('ps_contacts', 'endpoint')
+ op.alter_column('ps_contacts', 'expiration_time', type_=sa.String(40))
diff --git a/contrib/scripts/install_prereq b/contrib/scripts/install_prereq
index bda28e9f7..fb240890b 100755
--- a/contrib/scripts/install_prereq
+++ b/contrib/scripts/install_prereq
@@ -175,7 +175,9 @@ install_unpackaged() {
cd libsrtp
git pull
fi
- ./configure CFLAGS=-fPIC && make libsrtp.a && make uninstall && make install
+ git checkout "1_5_x_throttle"
+ ./configure --disable-debug --disable-stdout --enable-openssl
+ make shared_library uninstall install
cd ..
echo "/usr/local/lib" > /etc/ld.so.conf.d/usr_local.conf
/sbin/ldconfig
diff --git a/contrib/scripts/sip_to_pjsip/astconfigparser.py b/contrib/scripts/sip_to_pjsip/astconfigparser.py
index b207b0d7c..dc79e8032 100644
--- a/contrib/scripts/sip_to_pjsip/astconfigparser.py
+++ b/contrib/scripts/sip_to_pjsip/astconfigparser.py
@@ -1,3 +1,10 @@
+"""
+Copyright (C) 2016, Digium, Inc.
+
+This program is free software, distributed under the terms of
+the GNU General Public License Version 2.
+"""
+
import re
import itertools
@@ -44,6 +51,12 @@ class Section(MultiOrderedDict):
"""
return cmp(self.id, other.id)
+ def __eq__(self, other):
+ """
+ Use self.id as means of determining equality
+ """
+ return self.id == other.id
+
def get(self, key, from_self=True, from_templates=True,
from_defaults=True):
"""
@@ -184,9 +197,17 @@ def remove_comment(line, is_comment):
# otherwise it was an embedded comment so combine
return ''.join([part[0].strip(), ' ', line]).rstrip(), False
- # check for eol comment
- return line.partition(COMMENT)[0].strip(), False
+ # find the first occurence of a comment that is not escaped
+ match = re.match(r'.*?([^\\];)', line)
+
+ if match:
+ # the end of where the real string is is where the comment starts
+ line = line[0:(match.end()-1)]
+ if line.startswith(";"):
+ # if the line is actually a comment just ignore it all
+ line = ""
+ return line.replace("\\", "").strip(), False
def try_include(line):
"""
@@ -224,7 +245,7 @@ def try_section(line):
def try_option(line):
"""Parses the line as an option, returning the key/value pair."""
- data = re.split('=>?', line)
+ data = re.split('=>?', line, 1)
# should split in two (key/val), but either way use first two elements
return data[0].rstrip(), data[1].lstrip()
diff --git a/doc/appdocsxml.dtd b/doc/appdocsxml.dtd
index 511930fe2..21f1b9d52 100644
--- a/doc/appdocsxml.dtd
+++ b/doc/appdocsxml.dtd
@@ -67,7 +67,7 @@
<!ELEMENT field (#PCDATA)>
<!ATTLIST field name CDATA #REQUIRED>
- <!ELEMENT info (para|note|warning|variablelist|enumlist|info|xi:include)*>
+ <!ELEMENT info (para|example|note|warning|variablelist|enumlist|info|xi:include)*>
<!ATTLIST info name CDATA #REQUIRED>
<!ATTLIST info language CDATA #REQUIRED>
<!ATTLIST info tech CDATA #REQUIRED>
diff --git a/funcs/func_cdr.c b/funcs/func_cdr.c
index dc865934f..e67bca318 100644
--- a/funcs/func_cdr.c
+++ b/funcs/func_cdr.c
@@ -223,9 +223,11 @@ STASIS_MESSAGE_TYPE_DEFN_LOCAL(cdr_prop_write_message_type);
static struct timeval cdr_retrieve_time(struct ast_channel *chan, const char *time_name)
{
- struct timeval time;
+ struct timeval time = { 0 };
char *value = NULL;
char tempbuf[128];
+ long int tv_sec;
+ long int tv_usec;
if (ast_strlen_zero(ast_channel_name(chan))) {
/* Format request on a dummy channel */
@@ -234,7 +236,11 @@ static struct timeval cdr_retrieve_time(struct ast_channel *chan, const char *ti
ast_cdr_getvar(ast_channel_name(chan), time_name, tempbuf, sizeof(tempbuf));
}
- if (sscanf(tempbuf, "%ld.%ld", &time.tv_sec, &time.tv_usec) != 2) {
+ /* time.tv_usec is suseconds_t, which could be int or long */
+ if (sscanf(tempbuf, "%ld.%ld", &tv_sec, &tv_usec) == 2) {
+ time.tv_sec = tv_sec;
+ time.tv_usec = tv_usec;
+ } else {
ast_log(AST_LOG_WARNING, "Failed to fully extract '%s' from CDR\n", time_name);
}
diff --git a/funcs/func_channel.c b/funcs/func_channel.c
index deecda61e..5efc09c4f 100644
--- a/funcs/func_channel.c
+++ b/funcs/func_channel.c
@@ -229,207 +229,41 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<enum name="linkedid">
<para>R/O returns the linkedid if available, otherwise returns the uniqueid.</para>
</enum>
- </enumlist>
- <para><emphasis>chan_sip</emphasis> provides the following additional options:</para>
- <enumlist>
- <enum name="peerip">
- <para>R/O Get the IP address of the peer.</para>
- </enum>
- <enum name="recvip">
- <para>R/O Get the source IP address of the peer.</para>
- </enum>
- <enum name="recvport">
- <para>R/O Get the source port of the peer.</para>
- </enum>
- <enum name="from">
- <para>R/O Get the URI from the From: header.</para>
- </enum>
- <enum name="uri">
- <para>R/O Get the URI from the Contact: header.</para>
- </enum>
- <enum name="useragent">
- <para>R/O Get the useragent.</para>
- </enum>
- <enum name="peername">
- <para>R/O Get the name of the peer.</para>
- </enum>
- <enum name="t38passthrough">
- <para>R/O <literal>1</literal> if T38 is offered or enabled in this channel,
- otherwise <literal>0</literal></para>
- </enum>
- <enum name="rtpqos">
- <para>R/O Get QOS information about the RTP stream</para>
- <para> This option takes two additional arguments:</para>
- <para> Argument 1:</para>
- <para> <literal>audio</literal> Get data about the audio stream</para>
- <para> <literal>video</literal> Get data about the video stream</para>
- <para> <literal>text</literal> Get data about the text stream</para>
- <para> Argument 2:</para>
- <para> <literal>local_ssrc</literal> Local SSRC (stream ID)</para>
- <para> <literal>local_lostpackets</literal> Local lost packets</para>
- <para> <literal>local_jitter</literal> Local calculated jitter</para>
- <para> <literal>local_maxjitter</literal> Local calculated jitter (maximum)</para>
- <para> <literal>local_minjitter</literal> Local calculated jitter (minimum)</para>
- <para> <literal>local_normdevjitter</literal>Local calculated jitter (normal deviation)</para>
- <para> <literal>local_stdevjitter</literal> Local calculated jitter (standard deviation)</para>
- <para> <literal>local_count</literal> Number of received packets</para>
- <para> <literal>remote_ssrc</literal> Remote SSRC (stream ID)</para>
- <para> <literal>remote_lostpackets</literal>Remote lost packets</para>
- <para> <literal>remote_jitter</literal> Remote reported jitter</para>
- <para> <literal>remote_maxjitter</literal> Remote calculated jitter (maximum)</para>
- <para> <literal>remote_minjitter</literal> Remote calculated jitter (minimum)</para>
- <para> <literal>remote_normdevjitter</literal>Remote calculated jitter (normal deviation)</para>
- <para> <literal>remote_stdevjitter</literal>Remote calculated jitter (standard deviation)</para>
- <para> <literal>remote_count</literal> Number of transmitted packets</para>
- <para> <literal>rtt</literal> Round trip time</para>
- <para> <literal>maxrtt</literal> Round trip time (maximum)</para>
- <para> <literal>minrtt</literal> Round trip time (minimum)</para>
- <para> <literal>normdevrtt</literal> Round trip time (normal deviation)</para>
- <para> <literal>stdevrtt</literal> Round trip time (standard deviation)</para>
- <para> <literal>all</literal> All statistics (in a form suited to logging,
- but not for parsing)</para>
- </enum>
- <enum name="rtpdest">
- <para>R/O Get remote RTP destination information.</para>
- <para> This option takes one additional argument:</para>
- <para> Argument 1:</para>
- <para> <literal>audio</literal> Get audio destination</para>
- <para> <literal>video</literal> Get video destination</para>
- <para> <literal>text</literal> Get text destination</para>
- <para> Defaults to <literal>audio</literal> if unspecified.</para>
- </enum>
- <enum name="rtpsource">
- <para>R/O Get source RTP destination information.</para>
- <para> This option takes one additional argument:</para>
- <para> Argument 1:</para>
- <para> <literal>audio</literal> Get audio destination</para>
- <para> <literal>video</literal> Get video destination</para>
- <para> <literal>text</literal> Get text destination</para>
- <para> Defaults to <literal>audio</literal> if unspecified.</para>
- </enum>
- </enumlist>
- <xi:include xpointer="xpointer(/docs/info[@name='PJSIPCHANNEL'])" />
- <para><emphasis>chan_iax2</emphasis> provides the following additional options:</para>
- <enumlist>
- <enum name="osptoken">
- <para>R/O Get the peer's osptoken.</para>
- </enum>
- <enum name="peerip">
- <para>R/O Get the peer's ip address.</para>
- </enum>
- <enum name="peername">
- <para>R/O Get the peer's username.</para>
- </enum>
- <enum name="secure_signaling">
- <para>R/O Get the if the IAX channel is secured.</para>
- </enum>
- <enum name="secure_media">
- <para>R/O Get the if the IAX channel is secured.</para>
- </enum>
- </enumlist>
- <para><emphasis>chan_dahdi</emphasis> provides the following additional options:</para>
- <enumlist>
- <enum name="dahdi_channel">
- <para>R/O DAHDI channel related to this channel.</para>
- </enum>
- <enum name="dahdi_span">
- <para>R/O DAHDI span related to this channel.</para>
- </enum>
- <enum name="dahdi_type">
- <para>R/O DAHDI channel type, one of:</para>
- <enumlist>
- <enum name="analog" />
- <enum name="mfc/r2" />
- <enum name="pri" />
- <enum name="pseudo" />
- <enum name="ss7" />
- </enumlist>
- </enum>
- <enum name="keypad_digits">
- <para>R/O PRI Keypad digits that came in with the SETUP message.</para>
- </enum>
- <enum name="reversecharge">
- <para>R/O PRI Reverse Charging Indication, one of:</para>
- <enumlist>
- <enum name="-1"> <para>None</para></enum>
- <enum name=" 1"> <para>Reverse Charging Requested</para></enum>
- </enumlist>
- </enum>
- <enum name="no_media_path">
- <para>R/O PRI Nonzero if the channel has no B channel.
- The channel is either on hold or a call waiting call.</para>
- </enum>
- <enum name="buffers">
- <para>W/O Change the channel's buffer policy (for the current call only)</para>
- <para>This option takes two arguments:</para>
- <para> Number of buffers,</para>
- <para> Buffer policy being one of:</para>
- <para> <literal>full</literal></para>
- <para> <literal>immediate</literal></para>
- <para> <literal>half</literal></para>
- </enum>
- <enum name="echocan_mode">
- <para>W/O Change the configuration of the active echo
- canceller on the channel (if any), for the current call
- only.</para>
- <para>Possible values are:</para>
- <para> <literal>on</literal> Normal mode (the echo canceller is actually reinitalized)</para>
- <para> <literal>off</literal> Disabled</para>
- <para> <literal>fax</literal> FAX/data mode (NLP disabled if possible, otherwise
- completely disabled)</para>
- <para> <literal>voice</literal> Voice mode (returns from FAX mode, reverting the changes that were made)</para>
- </enum>
- </enumlist>
- <para><emphasis>chan_ooh323</emphasis> provides the following additional options:</para>
- <enumlist>
- <enum name="faxdetect">
- <para>R/W Fax Detect</para>
- <para>Returns 0 or 1</para>
- <para>Write yes or no</para>
- </enum>
- <enum name="t38support">
- <para>R/W t38support</para>
- <para>Returns 0 or 1</para>
- <para>Write yes or no</para>
- </enum>
- <enum name="h323id_url">
- <para>R/0 Returns caller URL</para>
- </enum>
- <enum name="caller_h323id">
- <para>R/0 Returns caller h323id</para>
- </enum>
- <enum name="caller_dialeddigits">
- <para>R/0 Returns caller dialed digits</para>
- </enum>
- <enum name="caller_email">
- <para>R/0 Returns caller email</para>
- </enum>
- <enum name="callee_email">
- <para>R/0 Returns callee email</para>
- </enum>
- <enum name="callee_dialeddigits">
- <para>R/0 Returns callee dialed digits</para>
- </enum>
- <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>
+ <para>R/W The maximum number of forwards allowed.</para>
</enum>
</enumlist>
+ <xi:include xpointer="xpointer(/docs/info[@name='SIPCHANNEL'])" />
+ <xi:include xpointer="xpointer(/docs/info[@name='PJSIPCHANNEL'])" />
+ <xi:include xpointer="xpointer(/docs/info[@name='IAXCHANNEL'])" />
+ <xi:include xpointer="xpointer(/docs/info[@name='DAHDICHANNEL'])" />
+ <xi:include xpointer="xpointer(/docs/info[@name='OOH323CHANNEL'])" />
</parameter>
</syntax>
<description>
<para>Gets/sets various pieces of information about the channel, additional <replaceable>item</replaceable> may
be available from the channel driver; see its documentation for details. Any <replaceable>item</replaceable>
requested that is not available on the current channel will return an empty string.</para>
+ <example title="Standard CHANNEL item examples">
+ ; Push a hangup handler subroutine existing at dialplan
+ ; location default,s,1 onto the current channel
+ same => n,Set(CHANNEL(hangup_handler_push)=default,s,1)
+
+ ; Set the current tonezone to Germany (de)
+ same => n,Set(CHANNEL(tonezone)=de)
+
+ ; Set the allowed maximum number of forwarding attempts
+ same => n,Set(CHANNEL(max_forwards)=10)
+
+ ; If this channel is ejected from its next bridge, and if
+ ; the channel is not hung up, begin executing dialplan at
+ ; location default,after-bridge,1
+ same => n,Set(CHANNEL(after_bridge_goto)=default,after-bridge,1)
+
+ ; Log the current state of the channel
+ same => n,Log(NOTICE, This channel is: ${CHANNEL(state)})
+ </example>
+ <xi:include xpointer="xpointer(/docs/info[@name='PJSIPCHANNEL_EXAMPLES'])" />
</description>
</function>
***/
diff --git a/funcs/func_curl.c b/funcs/func_curl.c
index 61b05900b..c7a1c6e42 100644
--- a/funcs/func_curl.c
+++ b/funcs/func_curl.c
@@ -794,6 +794,7 @@ static struct ast_custom_function acf_curlopt = {
.write = acf_curlopt_write,
};
+#ifdef TEST_FRAMEWORK
AST_TEST_DEFINE(vulnerable_url)
{
const char *bad_urls [] = {
@@ -841,6 +842,7 @@ AST_TEST_DEFINE(vulnerable_url)
return res;
}
+#endif
static int unload_module(void)
{
diff --git a/funcs/func_env.c b/funcs/func_env.c
index 3c260a2fb..072714f19 100644
--- a/funcs/func_env.c
+++ b/funcs/func_env.c
@@ -624,7 +624,7 @@ static int file_read(struct ast_channel *chan, const char *cmd, char *data, stru
ast_log(LOG_ERROR, "Cannot seek to offset %" PRId64 ": %s\n", i, strerror(errno));
}
end = fread(fbuf, 1, sizeof(fbuf), ff);
- for (pos = (end < sizeof(fbuf) ? fbuf + end - 1 : fbuf + sizeof(fbuf) - 1); pos > fbuf - 1; pos--) {
+ for (pos = (end < sizeof(fbuf) ? fbuf + end - 1 : fbuf + sizeof(fbuf) - 1); pos >= fbuf; pos--) {
LINE_COUNTER(pos, format, count);
if (length < 0 && count * -1 == length) {
@@ -1024,7 +1024,7 @@ static int file_write(struct ast_channel *chan, const char *cmd, char *data, con
fclose(ff);
return -1;
}
- for (pos = fbuf + sizeof(fbuf) - 1; pos > fbuf - 1; pos--) {
+ for (pos = fbuf + sizeof(fbuf) - 1; pos >= fbuf; pos--) {
LINE_COUNTER(pos, newline_format, count);
if (length < 0 && count * -1 == length) {
diff --git a/funcs/func_odbc.c b/funcs/func_odbc.c
index b067f968a..e2ca7c823 100644
--- a/funcs/func_odbc.c
+++ b/funcs/func_odbc.c
@@ -388,9 +388,25 @@ static struct odbc_obj *get_odbc_obj(const char *dsn_name, struct dsn **dsn)
static inline void release_obj_or_dsn(struct odbc_obj **obj, struct dsn **dsn)
{
if (dsn && *dsn) {
+ /* If multiple connections are not enabled then the guarantee
+ * of a single connection already exists and holding on to the
+ * connection would prevent any other user from acquiring it
+ * indefinitely.
+ */
+ if (ast_odbc_get_max_connections((*dsn)->name) < 2) {
+ ast_odbc_release_obj((*dsn)->connection);
+ (*dsn)->connection = NULL;
+ }
ao2_unlock(*dsn);
ao2_ref(*dsn, -1);
*dsn = NULL;
+ /* Some callers may provide both an obj and dsn. To ensure that
+ * the connection is not released twice we set it to NULL here if
+ * present.
+ */
+ if (obj) {
+ *obj = NULL;
+ }
} else if (obj && *obj) {
ast_odbc_release_obj(*obj);
*obj = NULL;
diff --git a/include/asterisk.h b/include/asterisk.h
index 873ed5c34..c4cdfab9a 100644
--- a/include/asterisk.h
+++ b/include/asterisk.h
@@ -313,4 +313,11 @@ struct ast_sched_context;
#define __stringify_1(x) #x
#define __stringify(x) __stringify_1(x)
+/*!
+ * \brief Retrieve the PBX UUID
+ * \param pbx_uuid A buffer of at least AST_UUID_STR_LEN (36 + 1) size to receive the UUID
+ * \param length The buffer length
+ */
+int ast_pbx_uuid_get(char *pbx_uuid, int length);
+
#endif /* _ASTERISK_H */
diff --git a/include/asterisk/_private.h b/include/asterisk/_private.h
index ebee96cc4..9255dc1de 100644
--- a/include/asterisk/_private.h
+++ b/include/asterisk/_private.h
@@ -29,7 +29,7 @@ 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 */
-void ast_channels_init(void); /*!< Provided by channel.c */
+int ast_channels_init(void); /*!< Provided by channel.c */
void ast_builtins_init(void); /*!< Provided by cli.c */
int ast_cli_perms_init(int reload); /*!< Provided by cli.c */
int dnsmgr_init(void); /*!< Provided by dnsmgr.c */
diff --git a/include/asterisk/autoconfig.h.in b/include/asterisk/autoconfig.h.in
index a01131cc3..5e53dbc70 100644
--- a/include/asterisk/autoconfig.h.in
+++ b/include/asterisk/autoconfig.h.in
@@ -103,6 +103,9 @@
/* Define to 1 if your GCC C compiler supports the 'may_alias' attribute. */
#undef HAVE_ATTRIBUTE_may_alias
+/* Define to 1 if your GCC C compiler supports the 'noreturn' attribute. */
+#undef HAVE_ATTRIBUTE_noreturn
+
/* Define to 1 if your GCC C compiler supports the 'pure' attribute. */
#undef HAVE_ATTRIBUTE_pure
@@ -548,9 +551,6 @@
/* 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
@@ -587,6 +587,9 @@
/* Define if your system has pjsip_dlg_create_uas_and_inc_lock declared. */
#undef HAVE_PJSIP_DLG_CREATE_UAS_AND_INC_LOCK
+/* Define if your system has PJSIP_EVSUB_GRP_LOCK */
+#undef HAVE_PJSIP_EVSUB_GRP_LOCK
+
/* Define if your system has pjsip_endpt_set_ext_resolver declared. */
#undef HAVE_PJSIP_EXTERNAL_RESOLVER
@@ -705,6 +708,9 @@
*/
#undef HAVE_PTHREAD_MUTEX_RECURSIVE_NP
+/* Have PTHREAD_PRIO_INHERIT. */
+#undef HAVE_PTHREAD_PRIO_INHERIT
+
/* Define if your system has the PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
headers. */
#undef HAVE_PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
@@ -1208,9 +1214,21 @@
/* Defined if libcurl supports HTTPS */
#undef LIBCURL_PROTOCOL_HTTPS
+/* Defined if libcurl supports IMAP */
+#undef LIBCURL_PROTOCOL_IMAP
+
/* Defined if libcurl supports LDAP */
#undef LIBCURL_PROTOCOL_LDAP
+/* Defined if libcurl supports POP3 */
+#undef LIBCURL_PROTOCOL_POP3
+
+/* Defined if libcurl supports RTSP */
+#undef LIBCURL_PROTOCOL_RTSP
+
+/* Defined if libcurl supports SMTP */
+#undef LIBCURL_PROTOCOL_SMTP
+
/* Defined if libcurl supports TELNET */
#undef LIBCURL_PROTOCOL_TELNET
@@ -1249,9 +1267,6 @@
/* Define if your system needs braces around PTHREAD_ONCE_INIT */
#undef PTHREAD_ONCE_INIT_NEEDS_BRACES
-/* Define as the return type of signal handlers (`int' or `void'). */
-#undef RETSIGTYPE
-
/* Define to the type of arg 1 for `select'. */
#undef SELECT_TYPE_ARG1
diff --git a/include/asterisk/codec.h b/include/asterisk/codec.h
index fb2b7da38..28befec50 100644
--- a/include/asterisk/codec.h
+++ b/include/asterisk/codec.h
@@ -77,8 +77,6 @@ struct ast_codec {
unsigned int smooth;
/*! \brief The module that registered this codec */
struct ast_module *mod;
- /*! \brief A format name for a default sane format using this codec */
- const char *format_name;
};
/*!
diff --git a/include/asterisk/compiler.h b/include/asterisk/compiler.h
index 77b5de40e..6ceaa5f77 100644
--- a/include/asterisk/compiler.h
+++ b/include/asterisk/compiler.h
@@ -77,6 +77,12 @@
#define attribute_may_alias
#endif
+#ifdef HAVE_ATTRIBUTE_noreturn
+#define attribute_noreturn __attribute__((noreturn))
+#else
+#define attribute_noreturn
+#endif
+
/* Some older version of GNU gcc (3.3.5 on OpenBSD 4.3 for example) dont like 'NULL' as sentinel */
#define SENTINEL ((char *)NULL)
diff --git a/include/asterisk/config_options.h b/include/asterisk/config_options.h
index 30c042176..30d0c9142 100644
--- a/include/asterisk/config_options.h
+++ b/include/asterisk/config_options.h
@@ -445,6 +445,28 @@ enum aco_option_type {
* {endcode}
*/
OPT_UINT_T,
+
+ /*! \brief Type for default option handler for bools (ast_true/ast_false)
+ * \note aco_option_register flags:
+ * non-zero : process via ast_true
+ * 0 : process via ast_false
+ * aco_option_register varargs:
+ * FLDSET macro with the field of type int. It is important to note that the field
+ * cannot be a bitfield. If bitfields are required, they must be set via a custom handler.
+ *
+ * This is exactly the same as OPT_BOOL_T. The only difference is that when
+ * translated to a string, OPT_BOOL_T becomes "true" or "false"; OPT_YESNO_T becomes
+ * "yes" or "no".
+ *
+ * Example:
+ * {code}
+ * struct test_item {
+ * int enabled;
+ * };
+ * aco_option_register(&cfg_info, "enabled", ACO_EXACT, my_types, "no", OPT_YESNO_T, 1, FLDSET(struct test_item, enabled));
+ * {endcode}
+ */
+ OPT_YESNO_T,
};
/*! \brief A callback function for handling a particular option
diff --git a/include/asterisk/event_defs.h b/include/asterisk/event_defs.h
index 80a8d7dda..2d5c75a44 100644
--- a/include/asterisk/event_defs.h
+++ b/include/asterisk/event_defs.h
@@ -58,8 +58,10 @@ enum ast_event_type {
AST_EVENT_ACL_CHANGE = 0x0b,
/*! Send out a ping for debugging distributed events */
AST_EVENT_PING = 0x0c,
+ /*! A cluster discovery message */
+ AST_EVENT_CLUSTER_DISCOVERY = 0x0d,
/*! Number of event types. This should be the last event type + 1 */
- AST_EVENT_TOTAL = 0x0d,
+ AST_EVENT_TOTAL = 0x0e,
};
/*! \brief Event Information Element types */
@@ -302,8 +304,15 @@ enum ast_event_ie_type {
* Payload type: UINT
*/
AST_EVENT_IE_CACHABLE = 0x003d,
+
+ /*!
+ * \brief Cluster node ID
+ * Used by: Corosync
+ * Payload type: UINT
+ */
+ AST_EVENT_IE_NODE_ID = 0x003e,
/*! \brief Must be the last IE value +1 */
- AST_EVENT_IE_TOTAL = 0x003e,
+ AST_EVENT_IE_TOTAL = 0x003f,
};
/*!
diff --git a/include/asterisk/format_cache.h b/include/asterisk/format_cache.h
index 9f4e06a23..ff03bb4aa 100644
--- a/include/asterisk/format_cache.h
+++ b/include/asterisk/format_cache.h
@@ -224,6 +224,14 @@ extern struct ast_format *ast_format_t140_red;
extern struct ast_format *ast_format_none;
/*!
+ * \brief Built-in SILK format.
+ */
+extern struct ast_format *ast_format_silk8;
+extern struct ast_format *ast_format_silk12;
+extern struct ast_format *ast_format_silk16;
+extern struct ast_format *ast_format_silk24;
+
+/*!
* \brief Initialize format cache support within the core.
*
* \retval 0 success
diff --git a/include/asterisk/pbx.h b/include/asterisk/pbx.h
index d722e123f..1fc8df8c9 100644
--- a/include/asterisk/pbx.h
+++ b/include/asterisk/pbx.h
@@ -1598,6 +1598,18 @@ void pbx_live_dangerously(int new_live_dangerously);
*/
int ast_thread_inhibit_escalations(void);
+/*!
+ * \brief Swap the current thread escalation inhibit setting.
+ * \since 11.24.0
+ *
+ * \param inhibit New setting. Non-zero to inhibit.
+ *
+ * \retval 1 if dangerous function execution was inhibited.
+ * \retval 0 if dangerous function execution was allowed.
+ * \retval -1 on error.
+ */
+int ast_thread_inhibit_escalations_swap(int inhibit);
+
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
diff --git a/include/asterisk/res_fax.h b/include/asterisk/res_fax.h
index 2304da734..5119bfa6c 100644
--- a/include/asterisk/res_fax.h
+++ b/include/asterisk/res_fax.h
@@ -179,11 +179,11 @@ struct ast_fax_session_details {
unsigned int t38timeout;
/*! the id of the t.38 gateway framehook for this channel */
int gateway_id;
- /*! the timeout for this gateway in seconds */
+ /*! The timeout for this gateway in ms */
int gateway_timeout;
/*! the id of the faxdetect framehook for this channel */
int faxdetect_id;
- /*! The timeout for this fax detect in seconds */
+ /*! The timeout for this fax detect in ms */
int faxdetect_timeout;
/*! flags used for fax detection */
int faxdetect_flags;
diff --git a/include/asterisk/res_odbc.h b/include/asterisk/res_odbc.h
index 8c7b54950..137f7d4a5 100644
--- a/include/asterisk/res_odbc.h
+++ b/include/asterisk/res_odbc.h
@@ -243,4 +243,9 @@ int ast_odbc_text2isolation(const char *txt);
*/
const char *ast_odbc_isolation2text(int iso);
+/*!
+ * \brief Return the current configured maximum number of connections for a class
+ */
+unsigned int ast_odbc_get_max_connections(const char *name);
+
#endif /* _ASTERISK_RES_ODBC_H */
diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h
index e3eab7cb4..72a2ff351 100644
--- a/include/asterisk/res_pjsip.h
+++ b/include/asterisk/res_pjsip.h
@@ -256,6 +256,8 @@ struct ast_sip_contact {
int via_port;
/*! Content of the Call-ID header in REGISTER request */
AST_STRING_FIELD_EXTENDED(call_id);
+ /*! The name of the endpoint that added the contact */
+ AST_STRING_FIELD_EXTENDED(endpoint_name);
};
#define CONTACT_STATUS "contact_status"
@@ -269,7 +271,6 @@ enum ast_sip_contact_status_type {
UNKNOWN,
CREATED,
REMOVED,
- UPDATED,
};
/*!
@@ -292,6 +293,8 @@ struct ast_sip_contact_status {
char *aor;
/*! The original contact's URI */
char *uri;
+ /*! TRUE if the contact was refreshed. e.g., re-registered */
+ unsigned int refresh:1;
};
/*!
@@ -501,6 +504,8 @@ struct ast_sip_endpoint_subscription_configuration {
unsigned int minexpiry;
/*! Message waiting configuration */
struct ast_sip_mwi_configuration mwi;
+ /* Context for SUBSCRIBE requests */
+ char context[AST_MAX_CONTEXT];
};
/*!
@@ -740,10 +745,12 @@ struct ast_sip_endpoint {
unsigned int usereqphone;
/*! Do we send messages for connected line updates for unanswered incoming calls immediately to this endpoint? */
unsigned int rpid_immediate;
- /* Access control list */
+ /*! Access control list */
struct ast_acl_list *acl;
- /* Restrict what IPs are allowed in the Contact header (for registration) */
+ /*! Restrict what IPs are allowed in the Contact header (for registration) */
struct ast_acl_list *contact_acl;
+ /*! The number of seconds into call to disable fax detection. (0 = disabled) */
+ unsigned int faxdetect_timeout;
};
/*!
@@ -986,6 +993,16 @@ void ast_sip_unregister_endpoint_identifier(struct ast_sip_endpoint_identifier *
void *ast_sip_endpoint_alloc(const char *name);
/*!
+ * \brief Change state of a persistent endpoint.
+ *
+ * \param endpoint The SIP endpoint name to change state.
+ * \param state The new state
+ * \retval 0 Success
+ * \retval -1 Endpoint not found
+ */
+int ast_sip_persistent_endpoint_update_state(const char *endpoint_name, enum ast_endpoint_state state);
+
+/*!
* \brief Get a pointer to the PJSIP endpoint.
*
* This is useful when modules have specific information they need
@@ -2446,6 +2463,32 @@ int ast_sip_register_supplement(struct ast_sip_supplement *supplement);
void ast_sip_unregister_supplement(struct ast_sip_supplement *supplement);
/*!
+ * \brief Retrieve the global MWI taskprocessor high water alert trigger level.
+ *
+ * \since 13.12.0
+ *
+ * \retval the system MWI taskprocessor high water alert trigger level
+ */
+unsigned int ast_sip_get_mwi_tps_queue_high(void);
+
+/*!
+ * \brief Retrieve the global MWI taskprocessor low water clear alert level.
+ *
+ * \since 13.12.0
+ *
+ * \retval the system MWI taskprocessor low water clear alert level
+ */
+int ast_sip_get_mwi_tps_queue_low(void);
+
+/*!
+ * \brief Retrieve the global setting 'disable sending unsolicited mwi on startup'.
+ * \since 13.12.0
+ *
+ * \retval non zero if disable.
+ */
+unsigned int ast_sip_get_mwi_disable_initial_unsolicited(void);
+
+/*!
* \brief Retrieve the system debug setting (yes|no|host).
*
* \note returned string needs to be de-allocated by caller.
diff --git a/include/asterisk/res_pjsip_session.h b/include/asterisk/res_pjsip_session.h
index e4c54a173..7e65e6d7c 100644
--- a/include/asterisk/res_pjsip_session.h
+++ b/include/asterisk/res_pjsip_session.h
@@ -137,7 +137,7 @@ struct ast_sip_session {
struct ast_party_id id;
/*! Requested capabilities */
struct ast_format_cap *req_caps;
- /*! Optional DSP, used only for inband DTMF detection if configured */
+ /*! Optional DSP, used only for inband DTMF/Fax-CNG detection if configured */
struct ast_dsp *dsp;
/*! Whether the termination of the session should be deferred */
unsigned int defer_terminate:1;
diff --git a/include/asterisk/stasis_app.h b/include/asterisk/stasis_app.h
index 90ef82ebf..8ceeffba3 100644
--- a/include/asterisk/stasis_app.h
+++ b/include/asterisk/stasis_app.h
@@ -746,6 +746,15 @@ int stasis_app_bridge_playback_channel_add(struct ast_bridge *bridge,
struct stasis_app_control *control);
/*!
+ * \brief remove channel from list of ARI playback channels for bridges.
+ *
+ * \param bridge_id The unique ID of the bridge the playback channel is in.
+ * \param control The app control structure for the playback channel
+ */
+void stasis_app_bridge_playback_channel_remove(char *bridge_id,
+ struct stasis_app_control *control);
+
+/*!
* \brief Result codes used when adding/removing channels to/from bridges.
*/
enum stasis_app_control_channel_result {
diff --git a/include/asterisk/stasis_system.h b/include/asterisk/stasis_system.h
index 8c6e60f46..274c02e49 100644
--- a/include/asterisk/stasis_system.h
+++ b/include/asterisk/stasis_system.h
@@ -122,6 +122,12 @@ struct stasis_message_type *ast_cc_failure_type(void);
struct stasis_message_type *ast_cc_monitorfailed_type(void);
/*!
+ * \brief A \ref stasis_message_type for Cluster discovery
+ * \since 13.11.0
+ */
+struct stasis_message_type *ast_cluster_discovery_type(void);
+
+/*!
* \brief Initialize the stasis system topic and message types
* \retval 0 on success
* \retval -1 on failure
diff --git a/include/asterisk/taskprocessor.h b/include/asterisk/taskprocessor.h
index e51122269..7c79036b3 100644
--- a/include/asterisk/taskprocessor.h
+++ b/include/asterisk/taskprocessor.h
@@ -242,6 +242,38 @@ int ast_taskprocessor_push_local(struct ast_taskprocessor *tps,
int (*task_exe)(struct ast_taskprocessor_local *local), void *datap);
/*!
+ * \brief Indicate the taskprocessor is suspended.
+ *
+ * \since 13.12.0
+ *
+ * \param tps Task processor.
+ * \retval 0 success
+ * \retval -1 failure
+ */
+int ast_taskprocessor_suspend(struct ast_taskprocessor *tps);
+
+/*!
+ * \brief Indicate the taskprocessor is unsuspended.
+ *
+ * \since 13.12.0
+ *
+ * \param tps Task processor.
+ * \retval 0 success
+ * \retval -1 failure
+ */
+int ast_taskprocessor_unsuspend(struct ast_taskprocessor *tps);
+
+/*!
+ * \brief Get the task processor suspend status
+ *
+ * \since 13.12.0
+ *
+ * \param tps Task processor.
+ * \retval non-zero if the task processor is suspended
+ */
+int ast_taskprocessor_is_suspended(struct ast_taskprocessor *tps);
+
+/*!
* \brief Pop a task off the taskprocessor and execute it.
*
* \since 12.0.0
diff --git a/include/asterisk/utils.h b/include/asterisk/utils.h
index c7a473732..c3df4779f 100644
--- a/include/asterisk/utils.h
+++ b/include/asterisk/utils.h
@@ -843,9 +843,14 @@ struct ast_http_digest {
*/
int ast_parse_digest(const char *digest, struct ast_http_digest *d, int request, int pedantic);
+#ifdef DO_CRASH
+#define DO_CRASH_NORETURN attribute_noreturn
+#else
+#define DO_CRASH_NORETURN
+#endif
#ifdef AST_DEVMODE
-void __ast_assert_failed(int condition, const char *condition_str, const char *file, int line, const char *function);
+void DO_CRASH_NORETURN __ast_assert_failed(int condition, const char *condition_str, const char *file, int line, const char *function);
#define ast_assert(a) _ast_assert(a, # a, __FILE__, __LINE__, __PRETTY_FUNCTION__)
static void force_inline _ast_assert(int condition, const char *condition_str, const char *file, int line, const char *function)
{
@@ -864,7 +869,7 @@ static void force_inline _ast_assert(int condition, const char *condition_str, c
*
* \return Nothing
*/
-void ast_do_crash(void);
+void DO_CRASH_NORETURN ast_do_crash(void);
#include "asterisk/strings.h"
@@ -968,6 +973,14 @@ int ast_str_to_eid(struct ast_eid *eid, const char *s);
int ast_eid_cmp(const struct ast_eid *eid1, const struct ast_eid *eid2);
/*!
+ * \brief Check if EID is empty
+ *
+ * \return 1 if the EID is empty, zero otherwise
+ * \since 13.12.0
+ */
+int ast_eid_is_empty(const struct ast_eid *eid);
+
+/*!
* \brief Get current thread ID
* \return the ID if platform is supported, else -1
*/
diff --git a/main/aoc.c b/main/aoc.c
index 29c5e87b6..4ab931536 100644
--- a/main/aoc.c
+++ b/main/aoc.c
@@ -85,6 +85,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
</parameter>
<parameter name="SpecialCode" />
</syntax>
+ <see-also>
+ <ref type="managerEvent">AOC-D</ref>
+ <ref type="managerEvent">AOC-E</ref>
+ </see-also>
</managerEventInstance>
</managerEvent>
<managerEvent language="en_US" name="AOC-D">
@@ -138,6 +142,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
<parameter name="NumberOf" />
<parameter name="TypeOf" />
</syntax>
+ <see-also>
+ <ref type="manager">AOCMessage</ref>
+ <ref type="managerEvent">AOC-S</ref>
+ <ref type="managerEvent">AOC-E</ref>
+ </see-also>
</managerEventInstance>
</managerEvent>
<managerEvent language="en_US" name="AOC-E">
@@ -151,6 +160,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
<parameter name="ID" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='AOC-D']/managerEventInstance/syntax/parameter)" />
</syntax>
+ <see-also>
+ <ref type="manager">AOCMessage</ref>
+ <ref type="managerEvent">AOC-S</ref>
+ <ref type="managerEvent">AOC-D</ref>
+ </see-also>
</managerEventInstance>
</managerEvent>
***/
diff --git a/main/ast_expr2.c b/main/ast_expr2.c
index a9e4eff44..a3c715ac1 100644
--- a/main/ast_expr2.c
+++ b/main/ast_expr2.c
@@ -3668,13 +3668,22 @@ op_tildetilde (struct val *a, struct val *b)
/* strip double quotes from both -- */
strip_quotes(a);
strip_quotes(b);
-
+
vs = malloc(strlen(a->u.s)+strlen(b->u.s)+1);
+ if (vs == NULL) {
+ ast_log(LOG_WARNING, "malloc() failed\n");
+ free_value(a);
+ free_value(b);
+ return NULL;
+ }
+
strcpy(vs,a->u.s);
strcat(vs,b->u.s);
v = make_str(vs);
+ free(vs);
+
/* free arguments */
free_value(a);
free_value(b);
diff --git a/main/ast_expr2.y b/main/ast_expr2.y
index 869dfe9ea..4f6087773 100644
--- a/main/ast_expr2.y
+++ b/main/ast_expr2.y
@@ -1661,13 +1661,22 @@ op_tildetilde (struct val *a, struct val *b)
/* strip double quotes from both -- */
strip_quotes(a);
strip_quotes(b);
-
+
vs = malloc(strlen(a->u.s)+strlen(b->u.s)+1);
+ if (vs == NULL) {
+ ast_log(LOG_WARNING, "malloc() failed\n");
+ free_value(a);
+ free_value(b);
+ return NULL;
+ }
+
strcpy(vs,a->u.s);
strcat(vs,b->u.s);
v = make_str(vs);
+ free(vs);
+
/* free arguments */
free_value(a);
free_value(b);
diff --git a/main/asterisk.c b/main/asterisk.c
index 164e659ba..b8eedfa38 100644
--- a/main/asterisk.c
+++ b/main/asterisk.c
@@ -248,6 +248,7 @@ int daemon(int, int); /* defined in libresolv of all places */
#include "asterisk/endpoints.h"
#include "asterisk/codec.h"
#include "asterisk/format_cache.h"
+#include "asterisk/astdb.h"
#include "../defaults.h"
@@ -600,6 +601,11 @@ void ast_unregister_thread(void *id)
}
}
+int ast_pbx_uuid_get(char *pbx_uuid, int length)
+{
+ return ast_db_get("pbx", "UUID", pbx_uuid, length);
+}
+
/*! \brief Give an overview of core settings */
static char *handle_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
@@ -607,6 +613,7 @@ static char *handle_show_settings(struct ast_cli_entry *e, int cmd, struct ast_c
struct ast_tm tm;
char eid_str[128];
struct rlimit limits;
+ char pbx_uuid[AST_UUID_STR_LEN];
switch (cmd) {
case CLI_INIT:
@@ -619,6 +626,7 @@ static char *handle_show_settings(struct ast_cli_entry *e, int cmd, struct ast_c
}
ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
+ ast_pbx_uuid_get(pbx_uuid, sizeof(pbx_uuid));
ast_cli(a->fd, "\nPBX Core settings\n");
ast_cli(a->fd, "-----------------\n");
@@ -657,6 +665,7 @@ static char *handle_show_settings(struct ast_cli_entry *e, int cmd, struct ast_c
ast_cli(a->fd, " System: %s/%s built by %s on %s %s\n", ast_build_os, ast_build_kernel, ast_build_user, ast_build_machine, ast_build_date);
ast_cli(a->fd, " System name: %s\n", ast_config_AST_SYSTEM_NAME);
ast_cli(a->fd, " Entity ID: %s\n", eid_str);
+ ast_cli(a->fd, " PBX UUID: %s\n", pbx_uuid);
ast_cli(a->fd, " Default language: %s\n", ast_defaultlanguage);
ast_cli(a->fd, " Language prefix: %s\n", ast_language_is_prefix ? "Enabled" : "Disabled");
ast_cli(a->fd, " User name and group: %s/%s\n", ast_config_AST_RUN_USER, ast_config_AST_RUN_GROUP);
@@ -3743,10 +3752,10 @@ static void ast_readconfig(void)
} else if (!strcasecmp(v->name, "entityid")) {
struct ast_eid tmp_eid;
if (!ast_str_to_eid(&tmp_eid, v->value)) {
- ast_verbose("Successfully set global EID to '%s'\n", v->value);
ast_eid_default = tmp_eid;
- } else
- ast_verbose("Invalid Entity ID '%s' provided\n", v->value);
+ } else {
+ ast_log(LOG_WARNING, "Invalid Entity ID '%s' provided\n", v->value);
+ }
} else if (!strcasecmp(v->name, "lightbackground")) {
ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_LIGHT_BACKGROUND);
} else if (!strcasecmp(v->name, "forceblackbackground")) {
@@ -4298,13 +4307,22 @@ int main(int argc, char *argv[])
return 0;
}
+static inline void check_init(int init_result, const char *name)
+{
+ if (init_result) {
+ printf("%s initialization failed.\n%s", name, term_quit());
+ ast_run_atexits(0);
+ exit(init_result == -2 ? 2 : 1);
+ }
+}
+
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 */
+ char pbx_uuid[AST_UUID_STR_LEN];
/* 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
@@ -4394,15 +4412,8 @@ static void asterisk_daemon(int isroot, const char *runuser, const char *rungrou
register_config_cli();
read_config_maps();
- if (astobj2_init()) {
- printf("Failed: astobj2_init\n%s", term_quit());
- exit(1);
- }
-
- if (ast_named_locks_init()) {
- printf("Failed: ast_named_locks_init\n%s", term_quit());
- exit(1);
- }
+ check_init(astobj2_init(), "AO2");
+ check_init(ast_named_locks_init(), "Named Locks");
if (ast_opt_console) {
if (el_hist == NULL || el == NULL)
@@ -4410,6 +4421,21 @@ static void asterisk_daemon(int isroot, const char *runuser, const char *rungrou
ast_el_read_default_histfile();
}
+#ifdef AST_XML_DOCS
+ /* Load XML documentation. */
+ ast_xmldoc_load_documentation();
+#endif
+
+ check_init(astdb_init(), "ASTdb");
+
+ ast_uuid_init();
+
+ if (ast_pbx_uuid_get(pbx_uuid, sizeof(pbx_uuid))) {
+ ast_uuid_generate_str(pbx_uuid, sizeof(pbx_uuid));
+ ast_db_put("pbx", "UUID", pbx_uuid);
+ }
+ ast_verb(0, "PBX UUID: %s\n", pbx_uuid);
+
ast_json_init();
ast_ulaw_init();
ast_alaw_init();
@@ -4417,92 +4443,27 @@ static void asterisk_daemon(int isroot, const char *runuser, const char *rungrou
callerid_init();
ast_builtins_init();
- if (ast_utils_init()) {
- printf("Failed: ast_utils_init\n%s", term_quit());
- exit(1);
- }
-
- if (ast_tps_init()) {
- printf("Failed: ast_tps_init\n%s", term_quit());
- exit(1);
- }
-
- if (ast_fd_init()) {
- printf("Failed: ast_fd_init\n%s", term_quit());
- exit(1);
- }
-
- if (ast_pbx_init()) {
- printf("Failed: ast_pbx_init\n%s", term_quit());
- exit(1);
- }
-
+ check_init(ast_utils_init(), "Utilities");
+ check_init(ast_tps_init(), "Task Processor Core");
+ check_init(ast_fd_init(), "File Descriptor Debugging");
+ check_init(ast_pbx_init(), "ast_pbx_init");
#ifdef TEST_FRAMEWORK
- if (ast_test_init()) {
- printf("Failed: ast_test_init\n%s", term_quit());
- exit(1);
- }
+ check_init(ast_test_init(), "Test Framework");
#endif
-
- if (ast_translate_init()) {
- printf("Failed: ast_translate_init\n%s", term_quit());
- exit(1);
- }
+ check_init(ast_translate_init(), "Translator Core");
ast_aoc_cli_init();
- ast_uuid_init();
-
- if (ast_sorcery_init()) {
- printf("Failed: ast_sorcery_init\n%s", term_quit());
- exit(1);
- }
-
- if (ast_codec_init()) {
- printf("Failed: ast_codec_init\n%s", term_quit());
- exit(1);
- }
-
- if (ast_format_init()) {
- printf("Failed: ast_format_init\n%s", term_quit());
- exit(1);
- }
-
- if (ast_format_cache_init()) {
- printf("Failed: ast_format_cache_init\n%s", term_quit());
- exit(1);
- }
-
- if (ast_codec_builtin_init()) {
- printf("Failed: ast_codec_builtin_init\n%s", term_quit());
- exit(1);
- }
-
-#ifdef AST_XML_DOCS
- /* Load XML documentation. */
- ast_xmldoc_load_documentation();
-#endif
-
- aco_init();
-
- if (ast_bucket_init()) {
- printf("Failed: ast_bucket_init\n%s", term_quit());
- exit(1);
- }
- if (stasis_init()) {
- printf("Stasis initialization failed.\n%s", term_quit());
- exit(1);
- }
-
- if (ast_stasis_system_init()) {
- printf("Stasis system-level information initialization failed.\n%s", term_quit());
- exit(1);
- }
-
- if (ast_endpoint_stasis_init()) {
- printf("Endpoint initialization failed.\n%s", term_quit());
- exit(1);
- }
+ check_init(ast_sorcery_init(), "Sorcery");
+ check_init(ast_codec_init(), "Codecs");
+ check_init(ast_format_init(), "Formats");
+ check_init(ast_format_cache_init(), "Format Cache");
+ check_init(ast_codec_builtin_init(), "Built-in Codecs");
+ check_init(aco_init(), "Configuration Option Framework");
+ check_init(ast_bucket_init(), "Bucket API");
+ check_init(stasis_init(), "Stasis");
+ check_init(ast_stasis_system_init(), "Stasis system-level information");
+ check_init(ast_endpoint_stasis_init(), "Stasis Endpoint");
ast_makesocket();
/* GCC 4.9 gives a bogus "right-hand operand of comma expression has
@@ -4526,212 +4487,58 @@ static void asterisk_daemon(int isroot, const char *runuser, const char *rungrou
srand((unsigned int) getpid() + (unsigned int) time(NULL));
initstate((unsigned int) getpid() * 65536 + (unsigned int) time(NULL), randompool, sizeof(randompool));
- if (init_logger()) { /* Start logging subsystem */
- printf("Failed: init_logger\n%s", term_quit());
- exit(1);
- }
+ check_init(init_logger(), "Logger");
threadstorage_init();
- if (ast_rtp_engine_init()) {
- printf("Failed: ast_rtp_engine_init\n%s", term_quit());
- exit(1);
- }
+ check_init(ast_rtp_engine_init(), "RTP Engine");
ast_autoservice_init();
- if (ast_timing_init()) {
- printf("Failed: ast_timing_init\n%s", term_quit());
- exit(1);
- }
-
- if (ast_ssl_init()) {
- printf("Failed: ast_ssl_init\n%s", term_quit());
- exit(1);
- }
-
- if (ast_pj_init()) {
- printf("Failed: ast_pj_init\n%s", term_quit());
- exit(1);
- }
-
- if (app_init()) {
- printf("App core initialization failed.\n%s", term_quit());
- exit(1);
- }
-
- if (devstate_init()) {
- printf("Device state core initialization failed.\n%s", term_quit());
- exit(1);
- }
-
- if (astdb_init()) {
- printf("Failed: astdb_init\n%s", term_quit());
- exit(1);
- }
-
- if (ast_msg_init()) {
- printf("Failed: ast_msg_init\n%s", term_quit());
- exit(1);
- }
-
- /* initialize the data retrieval API */
- if (ast_data_init()) {
- printf("Failed: ast_data_init\n%s", term_quit());
- exit(1);
- }
-
- ast_channels_init();
-
- if (ast_endpoint_init()) {
- printf ("Failed: ast_endpoint_init\n%s", term_quit());
- exit(1);
- }
-
- if (ast_pickup_init()) {
- printf("Failed: ast_pickup_init\n%s", term_quit());
- exit(1);
- }
-
- if (ast_bridging_init()) {
- printf("Failed: ast_bridging_init\n%s", term_quit());
- exit(1);
- }
-
- if (ast_parking_stasis_init()) {
- printf("Failed: ast_parking_stasis_init\n%s", term_quit());
- exit(1);
- }
-
- if (ast_device_state_engine_init()) {
- printf("Failed: ast_device_state_engine_init\n%s", term_quit());
- exit(1);
- }
-
- if (ast_presence_state_engine_init()) {
- printf("Failed: ast_presence_state_engine_init\n%s", term_quit());
- exit(1);
- }
-
- if ((moduleresult = load_modules(1))) { /* Load modules, pre-load only */
- printf("Failed: load_modules\n%s", term_quit());
- exit(moduleresult == -2 ? 2 : 1);
- }
-
- if (ast_features_init()) {
- printf("Failed: ast_features_init\n%s", term_quit());
- exit(1);
- }
-
- if (dnsmgr_init()) { /* Initialize the DNS manager */
- printf("Failed: dnsmgr_init\n%s", term_quit());
- exit(1);
- }
-
- if (ast_security_stasis_init()) { /* Initialize Security Stasis Topic and Events */
- printf("Failed: ast_security_stasis_init\n%s", term_quit());
- exit(1);
- }
-
- if (ast_named_acl_init()) { /* Initialize the Named ACL system */
- printf("Failed: ast_named_acl_init\n%s", term_quit());
- exit(1);
- }
+ check_init(ast_timing_init(), "Timing");
+ check_init(ast_ssl_init(), "SSL");
+ check_init(ast_pj_init(), "Embedded PJProject");
+ check_init(app_init(), "App Core");
+ check_init(devstate_init(), "Device State Core");
+ check_init(ast_msg_init(), "Messaging API");
+ check_init(ast_data_init(), "Data Retrieval API");
+ check_init(ast_channels_init(), "Channel");
+ check_init(ast_endpoint_init(), "Endpoints");
+ check_init(ast_pickup_init(), "Call Pickup");
+ check_init(ast_bridging_init(), "Bridging");
+ check_init(ast_parking_stasis_init(), "Parking Core");
+ check_init(ast_device_state_engine_init(), "Device State Engine");
+ check_init(ast_presence_state_engine_init(), "Presence State Engine");
+ check_init(load_modules(1), "Module Preload");
+ check_init(ast_features_init(), "Call Features");
+ check_init(dnsmgr_init(), "DNS manager");
+ check_init(ast_security_stasis_init(), "Security Stasis Topic and Events");
+ check_init(ast_named_acl_init(), "Named ACL system");
ast_http_init(); /* Start the HTTP server, if needed */
- if (ast_indications_init()) {
- printf("Failed: ast_indications_init\n%s", term_quit());
- exit(1);
- }
-
- if (ast_cdr_engine_init()) {
- printf("Failed: ast_cdr_engine_init\n%s", term_quit());
- exit(1);
- }
+ check_init(ast_indications_init(), "Indication Tone Handling");
+ check_init(ast_cdr_engine_init(), "CDR Engine");
ast_dsp_init();
ast_udptl_init();
- if (ast_image_init()) {
- printf("Failed: ast_image_init\n%s", term_quit());
- exit(1);
- }
-
- if (ast_file_init()) {
- printf("Failed: ast_file_init\n%s", term_quit());
- exit(1);
- }
-
- if (load_pbx()) {
- printf("Failed: load_pbx\n%s", term_quit());
- exit(1);
- }
-
- if (load_pbx_builtins()) {
- printf("Failed: load_pbx_builtins\n%s", term_quit());
- exit(1);
- }
-
- if (load_pbx_functions_cli()) {
- printf("Failed: load_pbx_functions_cli\n%s", term_quit());
- exit(1);
- }
-
- if (load_pbx_variables()) {
- printf("Failed: load_pbx_variables\n%s", term_quit());
- exit(1);
- }
-
- if (load_pbx_switch()) {
- printf("Failed: load_pbx_switch\n%s", term_quit());
- exit(1);
- }
-
- if (load_pbx_app()) {
- printf("Failed: load_pbx_app\n%s", term_quit());
- exit(1);
- }
-
- if (load_pbx_hangup_handler()) {
- printf("Failed: load_pbx_hangup_handler\n%s", term_quit());
- exit(1);
- }
-
- if (ast_local_init()) {
- printf("Failed: ast_local_init\n%s", term_quit());
- exit(1);
- }
-
- if (ast_cel_engine_init()) {
- printf("Failed: ast_cel_engine_init\n%s", term_quit());
- exit(1);
- }
-
- if (init_manager()) {
- printf("Failed: init_manager\n%s", term_quit());
- exit(1);
- }
-
- if (ast_enum_init()) {
- printf("Failed: ast_enum_init\n%s", term_quit());
- exit(1);
- }
-
- if (ast_cc_init()) {
- printf("Failed: ast_cc_init\n%s", term_quit());
- exit(1);
- }
-
- if (ast_sounds_index_init()) {
- printf("Failed: ast_sounds_index_init\n%s", term_quit());
- exit(1);
- }
-
- if ((moduleresult = load_modules(0))) { /* Load modules */
- printf("%s", term_quit());
- exit(moduleresult == -2 ? 2 : 1);
- }
+ check_init(ast_image_init(), "Image");
+ check_init(ast_file_init(), "Generic File Format Support");
+ check_init(load_pbx(), "load_pbx");
+ check_init(load_pbx_builtins(), "Builtin PBX Applications");
+ check_init(load_pbx_functions_cli(), "PBX Functions Support");
+ check_init(load_pbx_variables(), "PBX Variables Support");
+ check_init(load_pbx_switch(), "PBX Switch Support");
+ check_init(load_pbx_app(), "PBX Application Support");
+ check_init(load_pbx_hangup_handler(), "PBX Hangup Handler Support");
+ check_init(ast_local_init(), "Local Proxy Channel Driver");
+ check_init(ast_cel_engine_init(), "CEL Engine");
+ check_init(init_manager(), "Asterisk Manager Interface");
+ check_init(ast_enum_init(), "ENUM Support");
+ check_init(ast_cc_init(), "Call Completion Supplementary Services");
+ check_init(ast_sounds_index_init(), "Sounds Indexer");
+ check_init(load_modules(0), "Module");
/* loads the cli_permissoins.conf file needed to implement cli restrictions. */
ast_cli_perms_init(0);
diff --git a/main/astobj2_hash.c b/main/astobj2_hash.c
index 1cd6ee249..341ff79e0 100644
--- a/main/astobj2_hash.c
+++ b/main/astobj2_hash.c
@@ -298,7 +298,7 @@ static enum ao2_container_insert hash_ao2_insert_node(struct ao2_container_hash
break;
case AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE:
SWAP(cur->common.obj, node->common.obj);
- ao2_t_ref(node, -1, "Discard the new node.");
+ __ao2_ref(node, -1);
return AO2_CONTAINER_INSERT_NODE_OBJ_REPLACED;
}
}
@@ -331,7 +331,7 @@ static enum ao2_container_insert hash_ao2_insert_node(struct ao2_container_hash
break;
case AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE:
SWAP(cur->common.obj, node->common.obj);
- ao2_t_ref(node, -1, "Discard the new node.");
+ __ao2_ref(node, -1);
return AO2_CONTAINER_INSERT_NODE_OBJ_REPLACED;
}
}
diff --git a/main/astobj2_rbtree.c b/main/astobj2_rbtree.c
index d8195d47f..a8d5e3ac1 100644
--- a/main/astobj2_rbtree.c
+++ b/main/astobj2_rbtree.c
@@ -1267,7 +1267,7 @@ static enum ao2_container_insert rb_ao2_insert_node(struct ao2_container_rbtree
break;
case AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE:
SWAP(cur->common.obj, node->common.obj);
- ao2_t_ref(node, -1, "Don't need the new node.");
+ __ao2_ref(node, -1);
return AO2_CONTAINER_INSERT_NODE_OBJ_REPLACED;
}
diff --git a/main/bridge.c b/main/bridge.c
index ee5ad735b..a0f239bf8 100644
--- a/main/bridge.c
+++ b/main/bridge.c
@@ -38,6 +38,10 @@
<description>
<para>Returns detailed information about the available bridging technologies.</para>
</description>
+ <see-also>
+ <ref type="manager">BridgeTechnologySuspend</ref>
+ <ref type="manager">BridgeTechnologyUnsuspend</ref>
+ </see-also>
</manager>
<manager name="BridgeTechnologySuspend" language="en_US">
<synopsis>
@@ -52,6 +56,10 @@
<description>
<para>Marks a bridging technology as suspended, which prevents subsequently created bridges from using it.</para>
</description>
+ <see-also>
+ <ref type="manager">BridgeTechnologySuspend</ref>
+ <ref type="manager">BridgeTechnologyUnsuspend</ref>
+ </see-also>
</manager>
<manager name="BridgeTechnologyUnsuspend" language="en_US">
<synopsis>
@@ -66,6 +74,10 @@
<description>
<para>Clears a previously suspended bridging technology, which allows subsequently created bridges to use it.</para>
</description>
+ <see-also>
+ <ref type="manager">BridgeTechnologyList</ref>
+ <ref type="manager">BridgeTechnologySuspend</ref>
+ </see-also>
</manager>
***/
diff --git a/main/bridge_channel.c b/main/bridge_channel.c
index 543988dde..2fafdf990 100644
--- a/main/bridge_channel.c
+++ b/main/bridge_channel.c
@@ -1543,8 +1543,13 @@ static void testsuite_notify_feature_success(struct ast_channel *chan, const cha
{
#ifdef TEST_FRAMEWORK
char *feature = "unknown";
- struct ast_featuremap_config *featuremap = ast_get_chan_featuremap_config(chan);
- struct ast_features_xfer_config *xfer = ast_get_chan_features_xfer_config(chan);
+ struct ast_featuremap_config *featuremap;
+ struct ast_features_xfer_config *xfer;
+
+ ast_channel_lock(chan);
+ featuremap = ast_get_chan_featuremap_config(chan);
+ xfer = ast_get_chan_features_xfer_config(chan);
+ ast_channel_unlock(chan);
if (featuremap) {
if (!strcmp(dtmf, featuremap->blindxfer)) {
diff --git a/main/channel.c b/main/channel.c
index 327ec64a6..853935da3 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -2692,10 +2692,28 @@ void ast_hangup(struct ast_channel *chan)
ast_channel_unref(chan);
}
+/*!
+ * \internal
+ * \brief Set channel answered time if not already set.
+ * \since 13.11.0
+ *
+ * \param chan Channel to set answered time.
+ *
+ * \return Nothing
+ */
+static void set_channel_answer_time(struct ast_channel *chan)
+{
+ if (ast_tvzero(ast_channel_answertime(chan))) {
+ struct timeval answertime;
+
+ answertime = ast_tvnow();
+ ast_channel_answertime_set(chan, &answertime);
+ }
+}
+
int ast_raw_answer(struct ast_channel *chan)
{
int res = 0;
- struct timeval answertime;
ast_channel_lock(chan);
@@ -2711,8 +2729,11 @@ int ast_raw_answer(struct ast_channel *chan)
return -1;
}
- answertime = ast_tvnow();
- ast_channel_answertime_set(chan, &answertime);
+ /*
+ * Mark when incoming channel answered so we can know how
+ * long the channel has been up.
+ */
+ set_channel_answer_time(chan);
ast_channel_unlock(chan);
@@ -3911,6 +3932,12 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
ast_frfree(f);
f = &ast_null_frame;
} else {
+ /*
+ * Mark when outgoing channel answered so we can know how
+ * long the channel has been up.
+ */
+ set_channel_answer_time(chan);
+
ast_setstate(chan, AST_STATE_UP);
}
} else if (f->subclass.integer == AST_CONTROL_READ_ACTION) {
@@ -7776,13 +7803,14 @@ static void channels_shutdown(void)
ast_channel_unregister(&surrogate_tech);
}
-void ast_channels_init(void)
+int ast_channels_init(void)
{
channels = ao2_container_alloc(NUM_CHANNEL_BUCKETS,
ast_channel_hash_cb, ast_channel_cmp_cb);
- if (channels) {
- ao2_container_register("channels", channels, prnt_channel_key);
+ if (!channels) {
+ return -1;
}
+ ao2_container_register("channels", channels, prnt_channel_key);
ast_channel_register(&surrogate_tech);
@@ -7796,6 +7824,7 @@ void ast_channels_init(void)
ast_register_cleanup(channels_shutdown);
+ return 0;
}
/*! \brief Print call group and pickup group ---*/
diff --git a/main/codec.c b/main/codec.c
index c8644fd34..c253233bb 100644
--- a/main/codec.c
+++ b/main/codec.c
@@ -49,6 +49,32 @@ static int codec_id = 1;
/*! \brief Registered codecs */
static struct ao2_container *codecs;
+/*!
+ * \internal
+ * \brief Internal codec structure
+ *
+ * External codecs won't know about the format_name field so the public
+ * ast_codec structure has to leave it out. This structure will be used
+ * for the internal codecs.
+ *
+ */
+struct internal_ast_codec {
+ /*! \brief Public codec structure. Must remain first. */
+ struct ast_codec external;
+ /*! \brief A format name for a default sane format using this codec */
+ const char *format_name;
+};
+
+/*!
+ * \internal
+ * \brief Internal function for registration with format name
+ *
+ * This function is only used by codec.c and codec_builtin.c and
+ * will be removed in Asterisk 14
+ */
+int __ast_codec_register_with_format(struct ast_codec *codec, const char *format_name,
+ struct ast_module *mod);
+
static int codec_hash(const void *obj, int flags)
{
const struct ast_codec *codec;
@@ -113,7 +139,7 @@ static int codec_cmp(void *obj, void *arg, int flags)
static char *show_codecs(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
struct ao2_iterator i;
- struct ast_codec *codec;
+ struct internal_ast_codec *codec;
switch (cmd) {
case CLI_INIT:
@@ -144,19 +170,19 @@ static char *show_codecs(struct ast_cli_entry *e, int cmd, struct ast_cli_args *
for (; (codec = ao2_iterator_next(&i)); ao2_ref(codec, -1)) {
if (a->argc == 4) {
if (!strcasecmp(a->argv[3], "audio")) {
- if (codec->type != AST_MEDIA_TYPE_AUDIO) {
+ if (codec->external.type != AST_MEDIA_TYPE_AUDIO) {
continue;
}
} else if (!strcasecmp(a->argv[3], "video")) {
- if (codec->type != AST_MEDIA_TYPE_VIDEO) {
+ if (codec->external.type != AST_MEDIA_TYPE_VIDEO) {
continue;
}
} else if (!strcasecmp(a->argv[3], "image")) {
- if (codec->type != AST_MEDIA_TYPE_IMAGE) {
+ if (codec->external.type != AST_MEDIA_TYPE_IMAGE) {
continue;
}
} else if (!strcasecmp(a->argv[3], "text")) {
- if (codec->type != AST_MEDIA_TYPE_TEXT) {
+ if (codec->external.type != AST_MEDIA_TYPE_TEXT) {
continue;
}
} else {
@@ -165,11 +191,11 @@ static char *show_codecs(struct ast_cli_entry *e, int cmd, struct ast_cli_args *
}
ast_cli(a->fd, "%8u %-5s %-12s %-16s (%s)\n",
- codec->id,
- ast_codec_media_type2str(codec->type),
- codec->name,
+ codec->external.id,
+ ast_codec_media_type2str(codec->external.type),
+ codec->external.name,
S_OR(codec->format_name, "no cached format"),
- codec->description);
+ codec->external.description);
}
ao2_iterator_destroy(&i);
@@ -190,7 +216,7 @@ static int codec_id_cmp(void *obj, void *arg, int flags)
static char *show_codec(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
int type_punned_codec;
- struct ast_codec *codec;
+ struct internal_ast_codec *codec;
switch (cmd) {
case CLI_INIT:
@@ -217,7 +243,7 @@ static char *show_codec(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a
return CLI_SUCCESS;
}
- ast_cli(a->fd, "%11u %s (%s)\n", (unsigned int) codec->id, codec->description,
+ ast_cli(a->fd, "%11u %s (%s)\n", (unsigned int) codec->external.id, codec->external.description,
S_OR(codec->format_name, "no format"));
ao2_ref(codec, -1);
@@ -263,8 +289,13 @@ static void codec_dtor(void *obj)
int __ast_codec_register(struct ast_codec *codec, struct ast_module *mod)
{
+ return __ast_codec_register_with_format(codec, NULL, mod);
+}
+
+int __ast_codec_register_with_format(struct ast_codec *codec, const char *format_name, struct ast_module *mod)
+{
SCOPED_AO2WRLOCK(lock, codecs);
- struct ast_codec *codec_new;
+ struct internal_ast_codec *codec_new;
/* Some types have specific requirements */
if (codec->type == AST_MEDIA_TYPE_UNKNOWN) {
@@ -293,8 +324,9 @@ int __ast_codec_register(struct ast_codec *codec, struct ast_module *mod)
codec->name, ast_codec_media_type2str(codec->type), codec->sample_rate);
return -1;
}
- *codec_new = *codec;
- codec_new->id = codec_id++;
+ codec_new->external = *codec;
+ codec_new->format_name = format_name;
+ codec_new->external.id = codec_id++;
ao2_link_flags(codecs, codec_new, OBJ_NOLOCK);
@@ -302,7 +334,7 @@ int __ast_codec_register(struct ast_codec *codec, struct ast_module *mod)
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);
+ ast_codec_media_type2str(codec->type), codec->name, codec->sample_rate, codec_new->external.id);
ao2_ref(codec_new, -1);
diff --git a/main/codec_builtin.c b/main/codec_builtin.c
index d7d253ab8..1d329bc3b 100644
--- a/main/codec_builtin.c
+++ b/main/codec_builtin.c
@@ -38,6 +38,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/format_cache.h"
#include "asterisk/frame.h"
+int __ast_codec_register_with_format(struct ast_codec *codec, const char *format_name,
+ struct ast_module *mod);
+
enum frame_type {
TYPE_HIGH, /* 0x0 */
TYPE_LOW, /* 0x1 */
@@ -769,13 +772,71 @@ static struct ast_codec t140 = {
.type = AST_MEDIA_TYPE_TEXT,
};
+static int silk_samples(struct ast_frame *frame)
+{
+ /* XXX This is likely not at all what's intended from this callback. However,
+ * since SILK is variable bit rate, I have no idea how to take a frame of data
+ * and determine the number of samples present. Instead, we base this on the
+ * sample rate of the codec and the expected number of samples to receive in 20ms.
+ * In testing, this has worked just fine.
+ */
+ return ast_format_get_sample_rate(frame->subclass.format) / 50;
+}
+
+static struct ast_codec silk8 = {
+ .name = "silk",
+ .description = "SILK Codec (8 KHz)",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 8000,
+ .minimum_ms = 20,
+ .maximum_ms = 100,
+ .default_ms = 20,
+ .minimum_bytes = 160,
+ .samples_count = silk_samples
+};
+
+static struct ast_codec silk12 = {
+ .name = "silk",
+ .description = "SILK Codec (12 KHz)",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 12000,
+ .minimum_ms = 20,
+ .maximum_ms = 100,
+ .default_ms = 20,
+ .minimum_bytes = 240,
+ .samples_count = silk_samples
+};
+
+static struct ast_codec silk16 = {
+ .name = "silk",
+ .description = "SILK Codec (16 KHz)",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 16000,
+ .minimum_ms = 20,
+ .maximum_ms = 100,
+ .default_ms = 20,
+ .minimum_bytes = 320,
+ .samples_count = silk_samples
+};
+
+static struct ast_codec silk24 = {
+ .name = "silk",
+ .description = "SILK Codec (24 KHz)",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 24000,
+ .minimum_ms = 20,
+ .maximum_ms = 100,
+ .default_ms = 20,
+ .minimum_bytes = 480,
+ .samples_count = silk_samples
+};
+
#define CODEC_REGISTER_AND_CACHE(codec) \
({ \
int __res_ ## __LINE__ = 0; \
struct ast_format *__fmt_ ## __LINE__; \
struct ast_codec *__codec_ ## __LINE__; \
- codec.format_name = (codec).name; \
- res |= __ast_codec_register(&(codec), NULL); \
+ res |= __ast_codec_register_with_format(&(codec), (codec).name, NULL); \
__codec_ ## __LINE__ = ast_codec_get((codec).name, (codec).type, (codec).sample_rate); \
__fmt_ ## __LINE__ = __codec_ ## __LINE__ ? ast_format_create(__codec_ ## __LINE__) : NULL; \
res |= ast_format_cache_set(__fmt_ ## __LINE__); \
@@ -789,8 +850,7 @@ static struct ast_codec t140 = {
int __res_ ## __LINE__ = 0; \
struct ast_format *__fmt_ ## __LINE__; \
struct ast_codec *__codec_ ## __LINE__; \
- codec.format_name = fmt_name; \
- res |= __ast_codec_register(&(codec), NULL); \
+ res |= __ast_codec_register_with_format(&(codec), fmt_name, NULL); \
__codec_ ## __LINE__ = ast_codec_get((codec).name, (codec).type, (codec).sample_rate); \
__fmt_ ## __LINE__ = ast_format_create_named((fmt_name), __codec_ ## __LINE__); \
res |= ast_format_cache_set(__fmt_ ## __LINE__); \
@@ -842,6 +902,10 @@ int ast_codec_builtin_init(void)
res |= CODEC_REGISTER_AND_CACHE(t140red);
res |= CODEC_REGISTER_AND_CACHE(t140);
res |= CODEC_REGISTER_AND_CACHE(none);
+ res |= CODEC_REGISTER_AND_CACHE_NAMED("silk8", silk8);
+ res |= CODEC_REGISTER_AND_CACHE_NAMED("silk12", silk12);
+ res |= CODEC_REGISTER_AND_CACHE_NAMED("silk16", silk16);
+ res |= CODEC_REGISTER_AND_CACHE_NAMED("silk24", silk24);
return res;
}
diff --git a/main/config_options.c b/main/config_options.c
index c8988c984..cc8e218f8 100644
--- a/main/config_options.c
+++ b/main/config_options.c
@@ -97,6 +97,7 @@ static char *aco_option_type_string[] = {
"IP Address", /* OPT_SOCKADDR_T, */
"String", /* OPT_STRINGFIELD_T, */
"Unsigned Integer", /* OPT_UINT_T, */
+ "Boolean", /* OPT_YESNO_T, */
};
void *aco_pending_config(struct aco_info *info)
@@ -139,6 +140,10 @@ static aco_option_handler ast_config_option_default_handler(enum aco_option_type
switch(type) {
case OPT_ACL_T: return acl_handler_fn;
case OPT_BOOL_T: return bool_handler_fn;
+ /* Reading from config files, BOOL and YESNO are handled exactly the
+ * same. Their difference is in how they are rendered to users
+ */
+ case OPT_YESNO_T: return bool_handler_fn;
case OPT_BOOLFLAG_T: return boolflag_handler_fn;
case OPT_CHAR_ARRAY_T: return chararray_handler_fn;
case OPT_CODEC_T: return codec_handler_fn;
diff --git a/main/dsp.c b/main/dsp.c
index 087416358..840a839bd 100644
--- a/main/dsp.c
+++ b/main/dsp.c
@@ -68,6 +68,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/utils.h"
#include "asterisk/options.h"
#include "asterisk/config.h"
+#include "asterisk/test.h"
/*! Number of goertzels for progress detect */
enum gsamp_size {
@@ -171,8 +172,7 @@ enum gsamp_thresh {
*/
#define DTMF_THRESHOLD 8.0e7
-#define FAX_THRESHOLD 8.0e7
-#define FAX_2ND_HARMONIC 2.0 /* 4dB */
+#define TONE_THRESHOLD 7.8e7
#define DEF_DTMF_NORMAL_TWIST 6.31 /* 8.0dB */
#define DEF_RELAX_DTMF_NORMAL_TWIST 6.31 /* 8.0dB */
@@ -187,8 +187,6 @@ enum gsamp_thresh {
#define DTMF_RELATIVE_PEAK_ROW 6.3 /* 8dB */
#define DTMF_RELATIVE_PEAK_COL 6.3 /* 8dB */
-#define DTMF_2ND_HARMONIC_ROW (relax ? 1.7 : 2.5) /* 4dB normal */
-#define DTMF_2ND_HARMONIC_COL 63.1 /* 18dB */
#define DTMF_TO_TOTAL_ENERGY 42.0
#define BELL_MF_THRESHOLD 1.6e9
@@ -203,7 +201,7 @@ enum gsamp_thresh {
* followed by a 3 second silent (2100 Hz OFF) period.
*/
#define FAX_TONE_CNG_FREQ 1100
-#define FAX_TONE_CNG_DURATION 500
+#define FAX_TONE_CNG_DURATION 500 /* ms */
#define FAX_TONE_CNG_DB 16
/* This signal may be sent by the Terminating FAX machine anywhere between
@@ -211,7 +209,7 @@ enum gsamp_thresh {
* of a 2100 Hz tone that is from 2.6 to 4 seconds in duration.
*/
#define FAX_TONE_CED_FREQ 2100
-#define FAX_TONE_CED_DURATION 2600
+#define FAX_TONE_CED_DURATION 2600 /* ms */
#define FAX_TONE_CED_DB 16
#define DEFAULT_SAMPLE_RATE 8000
@@ -241,9 +239,13 @@ static const int DEFAULT_SILENCE_THRESHOLD = 256;
#define CONFIG_FILE_NAME "dsp.conf"
typedef struct {
+ /*! The previous previous sample calculation (No binary point just plain int) */
int v2;
+ /*! The previous sample calculation (No binary point just plain int) */
int v3;
+ /*! v2 and v3 power of two exponent to keep value in int range */
int chunky;
+ /*! 15 bit fixed point goertzel coefficient = 2 * cos(2 * pi * freq / sample_rate) */
int fac;
} goertzel_state_t;
@@ -329,12 +331,22 @@ static inline void goertzel_sample(goertzel_state_t *s, short sample)
{
int v1;
+ /*
+ * Shift previous values so
+ * v1 is previous previous value
+ * v2 is previous value
+ * until the new v3 is calculated.
+ */
v1 = s->v2;
s->v2 = s->v3;
+ /* Discard the binary fraction introduced by s->fac */
s->v3 = (s->fac * s->v2) >> 15;
+ /* Scale sample to match previous values */
s->v3 = s->v3 - v1 + (sample >> s->chunky);
- if (abs(s->v3) > 32768) {
+
+ if (abs(s->v3) > (1 << 15)) {
+ /* The result is now too large so increase the chunky power. */
s->chunky++;
s->v3 = s->v3 >> 1;
s->v2 = s->v2 >> 1;
@@ -344,21 +356,26 @@ static inline void goertzel_sample(goertzel_state_t *s, short sample)
static inline float goertzel_result(goertzel_state_t *s)
{
goertzel_result_t r;
+
r.value = (s->v3 * s->v3) + (s->v2 * s->v2);
r.value -= ((s->v2 * s->v3) >> 15) * s->fac;
+ /*
+ * We have to double the exponent because we multiplied the
+ * previous sample calculation values together.
+ */
r.power = s->chunky * 2;
return (float)r.value * (float)(1 << r.power);
}
static inline void goertzel_init(goertzel_state_t *s, float freq, unsigned int sample_rate)
{
- s->v2 = s->v3 = s->chunky = 0.0;
+ s->v2 = s->v3 = s->chunky = 0;
s->fac = (int)(32768.0 * 2.0 * cos(2.0 * M_PI * freq / sample_rate));
}
static inline void goertzel_reset(goertzel_state_t *s)
{
- s->v2 = s->v3 = s->chunky = 0.0;
+ s->v2 = s->v3 = s->chunky = 0;
}
typedef struct {
@@ -581,10 +598,11 @@ static int tone_detect(struct ast_dsp *dsp, tone_detect_state_t *s, int16_t *amp
tone_energy *= 2.0;
s->energy *= s->block_size;
- ast_debug(10, "tone %d, Ew=%.2E, Et=%.2E, s/n=%10.2f\n", s->freq, tone_energy, s->energy, tone_energy / (s->energy - tone_energy));
+ ast_debug(10, "%d Hz tone %2d Ew=%.4E, Et=%.4E, s/n=%10.2f\n", s->freq, s->hit_count, tone_energy, s->energy, tone_energy / (s->energy - tone_energy));
hit = 0;
- if (tone_energy > s->energy * s->threshold) {
- ast_debug(10, "Hit! count=%d\n", s->hit_count);
+ if (TONE_THRESHOLD <= tone_energy
+ && tone_energy > s->energy * s->threshold) {
+ ast_debug(10, "%d Hz tone Hit! %2d Ew=%.4E, Et=%.4E, s/n=%10.2f\n", s->freq, s->hit_count, tone_energy, s->energy, tone_energy / (s->energy - tone_energy));
hit = 1;
}
@@ -603,7 +621,7 @@ static int tone_detect(struct ast_dsp *dsp, tone_detect_state_t *s, int16_t *amp
}
if (s->hit_count == s->hits_required) {
- ast_debug(1, "%d Hz done detected\n", s->freq);
+ ast_debug(1, "%d Hz tone detected\n", s->freq);
res = 1;
}
@@ -716,6 +734,10 @@ static int dtmf_detect(struct ast_dsp *dsp, digit_detect_state_t *s, int16_t amp
best_col = i;
}
}
+ ast_debug(10, "DTMF best '%c' Erow=%.4E Ecol=%.4E Erc=%.4E Et=%.4E\n",
+ dtmf_positions[(best_row << 2) + best_col],
+ row_energy[best_row], col_energy[best_col],
+ row_energy[best_row] + col_energy[best_col], s->td.dtmf.energy);
hit = 0;
/* Basic signal level test and the twist test */
if (row_energy[best_row] >= DTMF_THRESHOLD &&
@@ -736,6 +758,7 @@ static int dtmf_detect(struct ast_dsp *dsp, digit_detect_state_t *s, int16_t amp
(row_energy[best_row] + col_energy[best_col]) > DTMF_TO_TOTAL_ENERGY * s->td.dtmf.energy) {
/* Got a hit */
hit = dtmf_positions[(best_row << 2) + best_col];
+ ast_debug(10, "DTMF hit '%c'\n", hit);
}
}
@@ -1622,7 +1645,7 @@ done:
for (x = 0; x < len; x++) {
odata[x] = AST_LIN2MU((unsigned short) shortdata[x]);
}
- } else if (ast_format_cmp(af->subclass.format, ast_format_ulaw) == AST_FORMAT_CMP_EQUAL) {
+ } else if (ast_format_cmp(af->subclass.format, ast_format_alaw) == AST_FORMAT_CMP_EQUAL) {
for (x = 0; x < len; x++) {
odata[x] = AST_LIN2A((unsigned short) shortdata[x]);
}
@@ -1929,9 +1952,460 @@ int ast_dsp_get_threshold_from_settings(enum threshold which)
return thresholds[which];
}
+#ifdef TEST_FRAMEWORK
+static void test_tone_sample_gen(short *slin_buf, int samples, int rate, int freq, short amplitude)
+{
+ int idx;
+ double sample_step = 2.0 * M_PI * freq / rate;/* radians per step */
+
+ for (idx = 0; idx < samples; ++idx) {
+ slin_buf[idx] = amplitude * sin(sample_step * idx);
+ }
+}
+#endif
+
+#ifdef TEST_FRAMEWORK
+static void test_tone_sample_gen_add(short *slin_buf, int samples, int rate, int freq, short amplitude)
+{
+ int idx;
+ double sample_step = 2.0 * M_PI * freq / rate;/* radians per step */
+
+ for (idx = 0; idx < samples; ++idx) {
+ slin_buf[idx] += amplitude * sin(sample_step * idx);
+ }
+}
+#endif
+
+#ifdef TEST_FRAMEWORK
+static void test_dual_sample_gen(short *slin_buf, int samples, int rate, int f1, short a1, int f2, short a2)
+{
+ test_tone_sample_gen(slin_buf, samples, rate, f1, a1);
+ test_tone_sample_gen_add(slin_buf, samples, rate, f2, a2);
+}
+#endif
+
+#ifdef TEST_FRAMEWORK
+#define TONE_AMPLITUDE_MAX 0x7fff /* Max signed linear amplitude */
+#define TONE_AMPLITUDE_MIN 80 /* Min signed linear amplitude detectable */
+
+static int test_tone_amplitude_sweep(struct ast_test *test, struct ast_dsp *dsp, tone_detect_state_t *tone_state)
+{
+ short slin_buf[tone_state->block_size];
+ int result;
+ int idx;
+ struct {
+ short amp_val;
+ int detect;
+ } amp_tests[] = {
+ { .amp_val = TONE_AMPLITUDE_MAX, .detect = 1, },
+ { .amp_val = 10000, .detect = 1, },
+ { .amp_val = 1000, .detect = 1, },
+ { .amp_val = 100, .detect = 1, },
+ { .amp_val = TONE_AMPLITUDE_MIN, .detect = 1, },
+ { .amp_val = 75, .detect = 0, },
+ { .amp_val = 10, .detect = 0, },
+ { .amp_val = 1, .detect = 0, },
+ };
+
+ result = 0;
+
+ for (idx = 0; idx < ARRAY_LEN(amp_tests); ++idx) {
+ int detected;
+ int duration;
+
+ ast_debug(1, "Test %d Hz at amplitude %d\n",
+ tone_state->freq, amp_tests[idx].amp_val);
+ test_tone_sample_gen(slin_buf, tone_state->block_size, DEFAULT_SAMPLE_RATE,
+ tone_state->freq, amp_tests[idx].amp_val);
+
+ detected = 0;
+ for (duration = 0; !detected && duration < tone_state->hits_required + 3; ++duration) {
+ detected = tone_detect(dsp, tone_state, slin_buf, tone_state->block_size) ? 1 : 0;
+ }
+ if (amp_tests[idx].detect != detected) {
+ /*
+ * Both messages are needed. ast_debug for when figuring out
+ * what went wrong and the test update for normal output before
+ * you start debugging. The different logging methods are not
+ * synchronized.
+ */
+ ast_debug(1,
+ "Test %d Hz at amplitude %d failed. Detected: %s\n",
+ tone_state->freq, amp_tests[idx].amp_val,
+ detected ? "yes" : "no");
+ ast_test_status_update(test,
+ "Test %d Hz at amplitude %d failed. Detected: %s\n",
+ tone_state->freq, amp_tests[idx].amp_val,
+ detected ? "yes" : "no");
+ result = -1;
+ }
+ tone_state->hit_count = 0;
+ }
+
+ return result;
+}
+#endif
+
+#ifdef TEST_FRAMEWORK
+static int test_dtmf_amplitude_sweep(struct ast_test *test, struct ast_dsp *dsp, int digit_index)
+{
+ short slin_buf[DTMF_GSIZE];
+ int result;
+ int row;
+ int column;
+ int idx;
+ struct {
+ short amp_val;
+ int digit;
+ } amp_tests[] = {
+ /*
+ * XXX Since there is no current DTMF level detection issue. This test
+ * just checks the current detection levels.
+ */
+ { .amp_val = TONE_AMPLITUDE_MAX/2, .digit = dtmf_positions[digit_index], },
+ { .amp_val = 10000, .digit = dtmf_positions[digit_index], },
+ { .amp_val = 1000, .digit = dtmf_positions[digit_index], },
+ { .amp_val = 500, .digit = dtmf_positions[digit_index], },
+ { .amp_val = 250, .digit = dtmf_positions[digit_index], },
+ { .amp_val = 200, .digit = dtmf_positions[digit_index], },
+ { .amp_val = 180, .digit = dtmf_positions[digit_index], },
+ /* Various digits detect and not detect in this range */
+ { .amp_val = 170, .digit = 0, },
+ { .amp_val = 100, .digit = 0, },
+ /*
+ * Amplitudes below TONE_AMPLITUDE_MIN start having questionable detection
+ * over quantization and background noise.
+ */
+ { .amp_val = TONE_AMPLITUDE_MIN, .digit = 0, },
+ { .amp_val = 75, .digit = 0, },
+ { .amp_val = 10, .digit = 0, },
+ { .amp_val = 1, .digit = 0, },
+ };
+
+ row = (digit_index >> 2) & 0x03;
+ column = digit_index & 0x03;
+
+ result = 0;
+
+ for (idx = 0; idx < ARRAY_LEN(amp_tests); ++idx) {
+ int digit;
+ int duration;
+
+ ast_debug(1, "Test '%c' at amplitude %d\n",
+ dtmf_positions[digit_index], amp_tests[idx].amp_val);
+ test_dual_sample_gen(slin_buf, ARRAY_LEN(slin_buf), DEFAULT_SAMPLE_RATE,
+ (int) dtmf_row[row], amp_tests[idx].amp_val,
+ (int) dtmf_col[column], amp_tests[idx].amp_val);
+
+ digit = 0;
+ for (duration = 0; !digit && duration < 3; ++duration) {
+ digit = dtmf_detect(dsp, &dsp->digit_state, slin_buf, ARRAY_LEN(slin_buf),
+ 0, 0);
+ }
+ if (amp_tests[idx].digit != digit) {
+ /*
+ * Both messages are needed. ast_debug for when figuring out
+ * what went wrong and the test update for normal output before
+ * you start debugging. The different logging methods are not
+ * synchronized.
+ */
+ ast_debug(1,
+ "Test '%c' at amplitude %d failed. Detected Digit: '%c'\n",
+ dtmf_positions[digit_index], amp_tests[idx].amp_val,
+ digit ?: ' ');
+ ast_test_status_update(test,
+ "Test '%c' at amplitude %d failed. Detected Digit: '%c'\n",
+ dtmf_positions[digit_index], amp_tests[idx].amp_val,
+ digit ?: ' ');
+ result = -1;
+ }
+ ast_dsp_digitreset(dsp);
+ }
+
+ return result;
+}
+#endif
+
+#ifdef TEST_FRAMEWORK
+static int test_dtmf_twist_sweep(struct ast_test *test, struct ast_dsp *dsp, int digit_index)
+{
+ short slin_buf[DTMF_GSIZE];
+ int result;
+ int row;
+ int column;
+ int idx;
+ struct {
+ short amp_row;
+ short amp_col;
+ int digit;
+ } twist_tests[] = {
+ /*
+ * XXX Since there is no current DTMF twist detection issue. This test
+ * just checks the current detection levels.
+ *
+ * Normal twist has the column higher than the row amplitude.
+ * Reverse twist is the other way.
+ */
+ { .amp_row = 1000 + 1800, .amp_col = 1000 + 0, .digit = 0, },
+ { .amp_row = 1000 + 1700, .amp_col = 1000 + 0, .digit = 0, },
+ /* Various digits detect and not detect in this range */
+ { .amp_row = 1000 + 1400, .amp_col = 1000 + 0, .digit = dtmf_positions[digit_index], },
+ { .amp_row = 1000 + 1300, .amp_col = 1000 + 0, .digit = dtmf_positions[digit_index], },
+ { .amp_row = 1000 + 1200, .amp_col = 1000 + 0, .digit = dtmf_positions[digit_index], },
+ { .amp_row = 1000 + 1100, .amp_col = 1000 + 0, .digit = dtmf_positions[digit_index], },
+ { .amp_row = 1000 + 1000, .amp_col = 1000 + 0, .digit = dtmf_positions[digit_index], },
+ { .amp_row = 1000 + 100, .amp_col = 1000 + 0, .digit = dtmf_positions[digit_index], },
+ { .amp_row = 1000 + 0, .amp_col = 1000 + 100, .digit = dtmf_positions[digit_index], },
+ { .amp_row = 1000 + 0, .amp_col = 1000 + 200, .digit = dtmf_positions[digit_index], },
+ { .amp_row = 1000 + 0, .amp_col = 1000 + 300, .digit = dtmf_positions[digit_index], },
+ { .amp_row = 1000 + 0, .amp_col = 1000 + 400, .digit = dtmf_positions[digit_index], },
+ { .amp_row = 1000 + 0, .amp_col = 1000 + 500, .digit = dtmf_positions[digit_index], },
+ { .amp_row = 1000 + 0, .amp_col = 1000 + 550, .digit = dtmf_positions[digit_index], },
+ /* Various digits detect and not detect in this range */
+ { .amp_row = 1000 + 0, .amp_col = 1000 + 650, .digit = 0, },
+ { .amp_row = 1000 + 0, .amp_col = 1000 + 700, .digit = 0, },
+ { .amp_row = 1000 + 0, .amp_col = 1000 + 800, .digit = 0, },
+ };
+ float save_normal_twist;
+ float save_reverse_twist;
+
+ save_normal_twist = dtmf_normal_twist;
+ save_reverse_twist = dtmf_reverse_twist;
+ dtmf_normal_twist = DEF_DTMF_NORMAL_TWIST;
+ dtmf_reverse_twist = DEF_DTMF_REVERSE_TWIST;
+
+ row = (digit_index >> 2) & 0x03;
+ column = digit_index & 0x03;
+
+ result = 0;
+
+ for (idx = 0; idx < ARRAY_LEN(twist_tests); ++idx) {
+ int digit;
+ int duration;
+
+ ast_debug(1, "Test '%c' twist row %d col %d amplitudes\n",
+ dtmf_positions[digit_index],
+ twist_tests[idx].amp_row, twist_tests[idx].amp_col);
+ test_dual_sample_gen(slin_buf, ARRAY_LEN(slin_buf), DEFAULT_SAMPLE_RATE,
+ (int) dtmf_row[row], twist_tests[idx].amp_row,
+ (int) dtmf_col[column], twist_tests[idx].amp_col);
+
+ digit = 0;
+ for (duration = 0; !digit && duration < 3; ++duration) {
+ digit = dtmf_detect(dsp, &dsp->digit_state, slin_buf, ARRAY_LEN(slin_buf),
+ 0, 0);
+ }
+ if (twist_tests[idx].digit != digit) {
+ /*
+ * Both messages are needed. ast_debug for when figuring out
+ * what went wrong and the test update for normal output before
+ * you start debugging. The different logging methods are not
+ * synchronized.
+ */
+ ast_debug(1,
+ "Test '%c' twist row %d col %d amplitudes failed. Detected Digit: '%c'\n",
+ dtmf_positions[digit_index],
+ twist_tests[idx].amp_row, twist_tests[idx].amp_col,
+ digit ?: ' ');
+ ast_test_status_update(test,
+ "Test '%c' twist row %d col %d amplitudes failed. Detected Digit: '%c'\n",
+ dtmf_positions[digit_index],
+ twist_tests[idx].amp_row, twist_tests[idx].amp_col,
+ digit ?: ' ');
+ result = -1;
+ }
+ ast_dsp_digitreset(dsp);
+ }
+
+ dtmf_normal_twist = save_normal_twist;
+ dtmf_reverse_twist = save_reverse_twist;
+
+ return result;
+}
+#endif
+
+#ifdef TEST_FRAMEWORK
+static int test_tone_freq_sweep(struct ast_test *test, struct ast_dsp *dsp, tone_detect_state_t *tone_state, short amplitude)
+{
+ short slin_buf[tone_state->block_size];
+ int result;
+ int freq;
+ int lower_freq;
+ int upper_freq;
+
+ /* Calculate detection frequency range */
+ lower_freq = tone_state->freq - 4;
+ upper_freq = tone_state->freq + 4;
+
+ result = 0;
+
+ /* Sweep frequencies loop. */
+ for (freq = 100; freq <= 3500; freq += 1) {
+ int detected;
+ int duration;
+ int expect_detection;
+
+ if (freq == tone_state->freq) {
+ /* This case is done by the amplitude sweep. */
+ continue;
+ }
+
+ expect_detection = (lower_freq <= freq && freq <= upper_freq) ? 1 : 0;
+
+ ast_debug(1, "Test %d Hz detection given %d Hz tone at amplitude %d. Range:%d-%d Expect detect: %s\n",
+ tone_state->freq, freq, amplitude, lower_freq, upper_freq,
+ expect_detection ? "yes" : "no");
+ test_tone_sample_gen(slin_buf, tone_state->block_size, DEFAULT_SAMPLE_RATE, freq,
+ amplitude);
+
+ detected = 0;
+ for (duration = 0; !detected && duration < tone_state->hits_required + 3; ++duration) {
+ detected = tone_detect(dsp, tone_state, slin_buf, tone_state->block_size) ? 1 : 0;
+ }
+ if (expect_detection != detected) {
+ /*
+ * Both messages are needed. ast_debug for when figuring out
+ * what went wrong and the test update for normal output before
+ * you start debugging. The different logging methods are not
+ * synchronized.
+ */
+ ast_debug(1,
+ "Test %d Hz detection given %d Hz tone at amplitude %d failed. Range:%d-%d Detected: %s\n",
+ tone_state->freq, freq, amplitude, lower_freq, upper_freq,
+ detected ? "yes" : "no");
+ ast_test_status_update(test,
+ "Test %d Hz detection given %d Hz tone at amplitude %d failed. Range:%d-%d Detected: %s\n",
+ tone_state->freq, freq, amplitude, lower_freq, upper_freq,
+ detected ? "yes" : "no");
+ result = -1;
+ }
+ tone_state->hit_count = 0;
+ }
+
+ return result;
+}
+#endif
+
+#ifdef TEST_FRAMEWORK
+AST_TEST_DEFINE(test_dsp_fax_detect)
+{
+ struct ast_dsp *dsp;
+ enum ast_test_result_state result;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "fax";
+ info->category = "/main/dsp/";
+ info->summary = "DSP fax tone detect unit test";
+ info->description =
+ "Tests fax tone detection code.";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ dsp = ast_dsp_new();
+ if (!dsp) {
+ return AST_TEST_FAIL;
+ }
+
+ result = AST_TEST_PASS;
+
+ /* Test CNG tone amplitude detection */
+ if (test_tone_amplitude_sweep(test, dsp, &dsp->cng_tone_state)) {
+ result = AST_TEST_FAIL;
+ }
+
+ /* Test CED tone amplitude detection */
+ if (test_tone_amplitude_sweep(test, dsp, &dsp->ced_tone_state)) {
+ result = AST_TEST_FAIL;
+ }
+
+ /* Test CNG tone frequency detection */
+ if (test_tone_freq_sweep(test, dsp, &dsp->cng_tone_state, TONE_AMPLITUDE_MAX)) {
+ result = AST_TEST_FAIL;
+ }
+ if (test_tone_freq_sweep(test, dsp, &dsp->cng_tone_state, TONE_AMPLITUDE_MIN)) {
+ result = AST_TEST_FAIL;
+ }
+
+ /* Test CED tone frequency detection */
+ if (test_tone_freq_sweep(test, dsp, &dsp->ced_tone_state, TONE_AMPLITUDE_MAX)) {
+ result = AST_TEST_FAIL;
+ }
+ if (test_tone_freq_sweep(test, dsp, &dsp->ced_tone_state, TONE_AMPLITUDE_MIN)) {
+ result = AST_TEST_FAIL;
+ }
+
+ ast_dsp_free(dsp);
+ return result;
+}
+#endif
+
+#ifdef TEST_FRAMEWORK
+AST_TEST_DEFINE(test_dsp_dtmf_detect)
+{
+ int idx;
+ struct ast_dsp *dsp;
+ enum ast_test_result_state result;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "dtmf";
+ info->category = "/main/dsp/";
+ info->summary = "DSP DTMF detect unit test";
+ info->description =
+ "Tests DTMF detection code.";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ dsp = ast_dsp_new();
+ if (!dsp) {
+ return AST_TEST_FAIL;
+ }
+
+ result = AST_TEST_PASS;
+
+ for (idx = 0; dtmf_positions[idx]; ++idx) {
+ if (test_dtmf_amplitude_sweep(test, dsp, idx)) {
+ result = AST_TEST_FAIL;
+ }
+ }
+
+ for (idx = 0; dtmf_positions[idx]; ++idx) {
+ if (test_dtmf_twist_sweep(test, dsp, idx)) {
+ result = AST_TEST_FAIL;
+ }
+ }
+
+ ast_dsp_free(dsp);
+ return result;
+}
+#endif
+
+#ifdef TEST_FRAMEWORK
+static void test_dsp_shutdown(void)
+{
+ AST_TEST_UNREGISTER(test_dsp_fax_detect);
+ AST_TEST_UNREGISTER(test_dsp_dtmf_detect);
+}
+#endif
+
int ast_dsp_init(void)
{
- return _dsp_init(0);
+ int res = _dsp_init(0);
+
+#ifdef TEST_FRAMEWORK
+ if (!res) {
+ AST_TEST_REGISTER(test_dsp_fax_detect);
+ AST_TEST_REGISTER(test_dsp_dtmf_detect);
+
+ ast_register_cleanup(test_dsp_shutdown);
+ }
+#endif
+ return res;
}
int ast_dsp_reload(void)
diff --git a/main/features.c b/main/features.c
index b96cbd68c..ea4a55baa 100644
--- a/main/features.c
+++ b/main/features.c
@@ -64,7 +64,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/cli.h"
#include "asterisk/manager.h"
#include "asterisk/utils.h"
-#include "asterisk/adsi.h"
#include "asterisk/devicestate.h"
#include "asterisk/audiohook.h"
#include "asterisk/global_datastores.h"
@@ -203,6 +202,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</variable>
</variablelist>
</description>
+ <see-also>
+ <ref type="manager">Bridge</ref>
+ <ref type="managerEvent">BridgeCreate</ref>
+ <ref type="managerEvent">BridgeEnter</ref>
+ </see-also>
</application>
<manager name="Bridge" language="en_US">
<synopsis>
@@ -229,6 +233,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<description>
<para>Bridge together two channels already in the PBX.</para>
</description>
+ <see-also>
+ <ref type="application">Bridge</ref>
+ <ref type="managerEvent">BridgeCreate</ref>
+ <ref type="managerEvent">BridgeEnter</ref>
+ <ref type="manager">BridgeDestroy</ref>
+ <ref type="manager">BridgeInfo</ref>
+ <ref type="manager">BridgeKick</ref>
+ <ref type="manager">BridgeList</ref>
+ </see-also>
</manager>
***/
@@ -767,8 +780,8 @@ static int action_bridge(struct mansession *s, const struct message *m)
astman_send_error(s, m, buf);
return 0;
}
- xfer_cfg_a = ast_get_chan_features_xfer_config(chana);
ast_channel_lock(chana);
+ xfer_cfg_a = ast_get_chan_features_xfer_config(chana);
chana_exten = ast_strdupa(ast_channel_exten(chana));
chana_context = ast_strdupa(ast_channel_context(chana));
chana_priority = ast_channel_priority(chana);
@@ -783,8 +796,8 @@ static int action_bridge(struct mansession *s, const struct message *m)
astman_send_error(s, m, buf);
return 0;
}
- xfer_cfg_b = ast_get_chan_features_xfer_config(chanb);
ast_channel_lock(chanb);
+ xfer_cfg_b = ast_get_chan_features_xfer_config(chanb);
chanb_exten = ast_strdupa(ast_channel_exten(chanb));
chanb_context = ast_strdupa(ast_channel_context(chanb));
chanb_priority = ast_channel_priority(chanb);
@@ -1098,7 +1111,9 @@ static int bridge_exec(struct ast_channel *chan, const char *data)
goto done;
}
+ ast_channel_lock(current_dest_chan);
xfer_cfg = ast_get_chan_features_xfer_config(current_dest_chan);
+ ast_channel_unlock(current_dest_chan);
bridge_add_failed = ast_bridge_add_channel(bridge, current_dest_chan, peer_features,
ast_test_flag(&opts, BRIDGE_OPT_PLAYTONE),
xfer_cfg ? xfer_cfg->xfersound : NULL);
diff --git a/main/format_cache.c b/main/format_cache.c
index 6638a78c0..74ebfe8d5 100644
--- a/main/format_cache.c
+++ b/main/format_cache.c
@@ -232,6 +232,14 @@ struct ast_format *ast_format_t140_red;
*/
struct ast_format *ast_format_none;
+/*!
+ * \brief Built-in "silk" format
+ */
+struct ast_format *ast_format_silk8;
+struct ast_format *ast_format_silk12;
+struct ast_format *ast_format_silk16;
+struct ast_format *ast_format_silk24;
+
/*! \brief Number of buckets to use for the media format cache (should be prime for performance reasons) */
#define CACHE_BUCKETS 53
@@ -331,6 +339,10 @@ static void format_cache_shutdown(void)
ao2_replace(ast_format_t140_red, NULL);
ao2_replace(ast_format_t140, NULL);
ao2_replace(ast_format_none, NULL);
+ ao2_replace(ast_format_silk8, NULL);
+ ao2_replace(ast_format_silk12, NULL);
+ ao2_replace(ast_format_silk16, NULL);
+ ao2_replace(ast_format_silk24, NULL);
}
int ast_format_cache_init(void)
@@ -426,6 +438,14 @@ static void set_cached_format(const char *name, struct ast_format *format)
ao2_replace(ast_format_t140, format);
} else if (!strcmp(name, "none")) {
ao2_replace(ast_format_none, format);
+ } else if (!strcmp(name, "silk8")) {
+ ao2_replace(ast_format_silk8, format);
+ } else if (!strcmp(name, "silk12")) {
+ ao2_replace(ast_format_silk12, format);
+ } else if (!strcmp(name, "silk16")) {
+ ao2_replace(ast_format_silk16, format);
+ } else if (!strcmp(name, "silk24")) {
+ ao2_replace(ast_format_silk24, format);
}
}
diff --git a/main/http.c b/main/http.c
index c343cb236..b2b35ff59 100644
--- a/main/http.c
+++ b/main/http.c
@@ -2220,7 +2220,7 @@ static int __ast_http_load(int reload)
* the non-TLS bindaddress here.
*/
if (ast_sockaddr_isnull(&https_desc.local_address) && http_desc.accept_fd != -1) {
- ast_sockaddr_copy(&https_desc.local_address, &https_desc.local_address);
+ ast_sockaddr_copy(&https_desc.local_address, &http_desc.local_address);
/* Of course, we can't use the same port though.
* Since no bind address was specified, we just use the
* default TLS port
diff --git a/main/manager.c b/main/manager.c
index bc4804d89..0ca84b2ca 100644
--- a/main/manager.c
+++ b/main/manager.c
@@ -149,6 +149,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<description>
<para>Logoff the current manager session.</para>
</description>
+ <see-also>
+ <ref type="manager">Login</ref>
+ </see-also>
</manager>
<manager name="Login" language="en_US">
<synopsis>
@@ -168,6 +171,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<description>
<para>Login Manager.</para>
</description>
+ <see-also>
+ <ref type="manager">Logoff</ref>
+ </see-also>
</manager>
<manager name="Challenge" language="en_US">
<synopsis>
@@ -321,6 +327,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>If a channel name is not provided then the variable is considered global.</para>
</note>
</description>
+ <see-also>
+ <ref type="manager">Getvar</ref>
+ </see-also>
</manager>
<manager name="Getvar" language="en_US">
<synopsis>
@@ -341,6 +350,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>If a channel name is not provided then the variable is considered global.</para>
</note>
</description>
+ <see-also>
+ <ref type="manager">Setvar</ref>
+ </see-also>
</manager>
<manager name="GetConfig" language="en_US">
<synopsis>
@@ -373,6 +385,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
In the case where a category name is non-unique, a filter may be specified
to match only categories with matching variable values.</para>
</description>
+ <see-also>
+ <ref type="manager">GetConfigJSON</ref>
+ <ref type="manager">UpdateConfig</ref>
+ <ref type="manager">CreateConfig</ref>
+ <ref type="manager">ListCategories</ref>
+ </see-also>
</manager>
<manager name="GetConfigJSON" language="en_US">
<synopsis>
@@ -397,6 +415,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
In the case where a category name is non-unique, a filter may be specified
to match only categories with matching variable values.</para>
</description>
+ <see-also>
+ <ref type="manager">GetConfig</ref>
+ <ref type="manager">UpdateConfig</ref>
+ <ref type="manager">CreateConfig</ref>
+ <ref type="manager">ListCategories</ref>
+ </see-also>
</manager>
<manager name="UpdateConfig" language="en_US">
<synopsis>
@@ -488,6 +512,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>This action will modify, create, or delete configuration elements
in Asterisk configuration files.</para>
</description>
+ <see-also>
+ <ref type="manager">GetConfig</ref>
+ <ref type="manager">GetConfigJSON</ref>
+ <ref type="manager">CreateConfig</ref>
+ <ref type="manager">ListCategories</ref>
+ </see-also>
</manager>
<manager name="CreateConfig" language="en_US">
<synopsis>
@@ -504,6 +534,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
directory. This action is intended to be used before an UpdateConfig
action.</para>
</description>
+ <see-also>
+ <ref type="manager">GetConfig</ref>
+ <ref type="manager">GetConfigJSON</ref>
+ <ref type="manager">UpdateConfig</ref>
+ <ref type="manager">ListCategories</ref>
+ </see-also>
</manager>
<manager name="ListCategories" language="en_US">
<synopsis>
@@ -518,6 +554,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<description>
<para>This action will dump the categories in a given file.</para>
</description>
+ <see-also>
+ <ref type="manager">GetConfig</ref>
+ <ref type="manager">GetConfigJSON</ref>
+ <ref type="manager">UpdateConfig</ref>
+ <ref type="manager">CreateConfig</ref>
+ </see-also>
</manager>
<manager name="Redirect" language="en_US">
<synopsis>
@@ -553,6 +595,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<description>
<para>Redirect (transfer) a call.</para>
</description>
+ <see-also>
+ <ref type="manager">BlindTransfer</ref>
+ </see-also>
</manager>
<manager name="Atxfer" language="en_US">
<synopsis>
@@ -573,6 +618,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<description>
<para>Attended transfer.</para>
</description>
+ <see-also>
+ <ref type="managerEvent">AttendedTransfer</ref>
+ </see-also>
</manager>
<manager name="Originate" language="en_US">
<synopsis>
@@ -697,6 +745,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>Will return an <literal>Extension Status</literal> message. The response will include
the hint for the extension and the status.</para>
</description>
+ <see-also>
+ <ref type="managerEvent">ExtensionStatus</ref>
+ </see-also>
</manager>
<manager name="PresenceState" language="en_US">
<synopsis>
@@ -713,6 +764,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>Will return a <literal>Presence State</literal> message. The response will include the
presence state and, if set, a presence subtype and custom message.</para>
</description>
+ <see-also>
+ <ref type="managerEvent">PresenceStatus</ref>
+ </see-also>
</manager>
<manager name="AbsoluteTimeout" language="en_US">
<synopsis>
@@ -750,6 +804,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>Waiting: <literal>0</literal> if messages waiting, <literal>1</literal>
if no messages waiting.</para>
</description>
+ <see-also>
+ <ref type="manager">MailboxCount</ref>
+ </see-also>
</manager>
<manager name="MailboxCount" language="en_US">
<synopsis>
@@ -770,6 +827,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>NewMessages: <replaceable>count</replaceable></para>
<para>OldMessages: <replaceable>count</replaceable></para>
</description>
+ <see-also>
+ <ref type="manager">MailboxStatus</ref>
+ </see-also>
</manager>
<manager name="ListCommands" language="en_US">
<synopsis>
@@ -819,6 +879,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<description>
<para>Send an event to manager sessions.</para>
</description>
+ <see-also>
+ <ref type="managerEvent">UserEvent</ref>
+ <ref type="application">UserEvent</ref>
+ </see-also>
</manager>
<manager name="WaitEvent" language="en_US">
<synopsis>
@@ -871,6 +935,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<description>
<para>Send a reload event.</para>
</description>
+ <see-also>
+ <ref type="manager">ModuleLoad</ref>
+ </see-also>
</manager>
<managerEvent language="en_US" name="CoreShowChannel">
<managerEventInstance class="EVENT_FLAG_CALL">
@@ -983,6 +1050,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<description>
<para>Loads, unloads or reloads an Asterisk module in a running system.</para>
</description>
+ <see-also>
+ <ref type="manager">Reload</ref>
+ <ref type="manager">ModuleCheck</ref>
+ </see-also>
</manager>
<manager name="ModuleCheck" language="en_US">
<synopsis>
@@ -998,6 +1069,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>Checks if Asterisk module is loaded. Will return Success/Failure.
For success returns, the module revision number is included.</para>
</description>
+ <see-also>
+ <ref type="manager">ModuleLoad</ref>
+ </see-also>
</manager>
<manager name="AOCMessage" language="en_US">
<synopsis>
@@ -1104,6 +1178,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<description>
<para>Generates an AOC-D or AOC-E message on a channel.</para>
</description>
+ <see-also>
+ <ref type="managerEvent">AOC-D</ref>
+ <ref type="managerEvent">AOC-E</ref>
+ </see-also>
</manager>
<function name="AMI_CLIENT" language="en_US">
<synopsis>
@@ -1163,6 +1241,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
this command can be used to create filters that may bypass
filters defined in manager.conf</para>
</description>
+ <see-also>
+ <ref type="manager">FilterList</ref>
+ </see-also>
</manager>
<manager name="FilterList" language="en_US">
<synopsis>
@@ -1172,6 +1253,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>The filters displayed are for the current session. Only those filters defined in
manager.conf will be present upon starting a new session.</para>
</description>
+ <see-also>
+ <ref type="manager">Filter</ref>
+ </see-also>
</manager>
<manager name="BlindTransfer" language="en_US">
<synopsis>
@@ -1190,6 +1274,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</description>
<see-also>
<ref type="manager">Redirect</ref>
+ <ref type="managerEvent">BlindTransfer</ref>
</see-also>
</manager>
<managerEvent name="ExtensionStatus" language="en_US">
@@ -1267,6 +1352,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</enumlist>
</parameter>
</syntax>
+ <see-also>
+ <ref type="manager">ExtensionState</ref>
+ </see-also>
</managerEventInstance>
</managerEvent>
<managerEvent name="PresenceStatus" language="en_US">
@@ -1280,6 +1368,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<parameter name="Subtype" />
<parameter name="Message" />
</syntax>
+ <see-also>
+ <ref type="manager">PresenceState</ref>
+ </see-also>
</managerEventInstance>
</managerEvent>
***/
diff --git a/main/manager_bridges.c b/main/manager_bridges.c
index e190baa83..8fcdf1320 100644
--- a/main/manager_bridges.c
+++ b/main/manager_bridges.c
@@ -42,6 +42,11 @@ static struct stasis_message_router *bridge_state_router;
<syntax>
<bridge_snapshot/>
</syntax>
+ <see-also>
+ <ref type="managerEvent">BridgeDestroy</ref>
+ <ref type="managerEvent">BridgeEnter</ref>
+ <ref type="managerEvent">BridgeLeave</ref>
+ </see-also>
</managerEventInstance>
</managerEvent>
<managerEvent language="en_US" name="BridgeDestroy">
@@ -50,6 +55,11 @@ static struct stasis_message_router *bridge_state_router;
<syntax>
<bridge_snapshot/>
</syntax>
+ <see-also>
+ <ref type="managerEvent">BridgeCreate</ref>
+ <ref type="managerEvent">BridgeEnter</ref>
+ <ref type="managerEvent">BridgeLeave</ref>
+ </see-also>
</managerEventInstance>
</managerEvent>
<managerEvent language="en_US" name="BridgeEnter">
@@ -62,6 +72,11 @@ static struct stasis_message_router *bridge_state_router;
<para>The uniqueid of the channel being swapped out of the bridge</para>
</parameter>
</syntax>
+ <see-also>
+ <ref type="managerEvent">BridgeCreate</ref>
+ <ref type="managerEvent">BridgeDestroy</ref>
+ <ref type="managerEvent">BridgeLeave</ref>
+ </see-also>
</managerEventInstance>
</managerEvent>
<managerEvent language="en_US" name="BridgeLeave">
@@ -71,6 +86,11 @@ static struct stasis_message_router *bridge_state_router;
<bridge_snapshot/>
<channel_snapshot/>
</syntax>
+ <see-also>
+ <ref type="managerEvent">BridgeCreate</ref>
+ <ref type="managerEvent">BridgeDestroy</ref>
+ <ref type="managerEvent">BridgeEnter</ref>
+ </see-also>
</managerEventInstance>
</managerEvent>
<manager name="BridgeList" language="en_US">
@@ -86,6 +106,12 @@ static struct stasis_message_router *bridge_state_router;
<description>
<para>Returns a list of bridges, optionally filtering on a bridge type.</para>
</description>
+ <see-also>
+ <ref type="manager">Bridge</ref>
+ <ref type="manager">BridgeDestroy</ref>
+ <ref type="manager">BridgeInfo</ref>
+ <ref type="manager">BridgeKick</ref>
+ </see-also>
</manager>
<manager name="BridgeInfo" language="en_US">
<synopsis>
@@ -100,6 +126,12 @@ static struct stasis_message_router *bridge_state_router;
<description>
<para>Returns detailed information about a bridge and the channels in it.</para>
</description>
+ <see-also>
+ <ref type="manager">Bridge</ref>
+ <ref type="manager">BridgeDestroy</ref>
+ <ref type="manager">BridgeKick</ref>
+ <ref type="manager">BridgeList</ref>
+ </see-also>
<responses>
<list-elements>
<managerEvent language="en_US" name="BridgeInfoChannel">
@@ -134,6 +166,13 @@ static struct stasis_message_router *bridge_state_router;
<description>
<para>Deletes the bridge, causing channels to continue or hang up.</para>
</description>
+ <see-also>
+ <ref type="manager">Bridge</ref>
+ <ref type="manager">BridgeInfo</ref>
+ <ref type="manager">BridgeKick</ref>
+ <ref type="manager">BridgeList</ref>
+ <ref type="managerEvent">BridgeDestroy</ref>
+ </see-also>
</manager>
<manager name="BridgeKick" language="en_US">
<synopsis>
@@ -153,6 +192,13 @@ static struct stasis_message_router *bridge_state_router;
<description>
<para>The channel is removed from the bridge.</para>
</description>
+ <see-also>
+ <ref type="manager">Bridge</ref>
+ <ref type="manager">BridgeDestroy</ref>
+ <ref type="manager">BridgeInfo</ref>
+ <ref type="manager">BridgeList</ref>
+ <ref type="managerEvent">BridgeLeave</ref>
+ </see-also>
</manager>
***/
diff --git a/main/manager_channels.c b/main/manager_channels.c
index ef71c65b1..7a4bd23fb 100644
--- a/main/manager_channels.c
+++ b/main/manager_channels.c
@@ -44,6 +44,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<syntax>
<channel_snapshot/>
</syntax>
+ <see-also>
+ <ref type="managerEvent">Newstate</ref>
+ <ref type="managerEvent">Hangup</ref>
+ </see-also>
</managerEventInstance>
</managerEvent>
<managerEvent language="en_US" name="Newstate">
@@ -52,6 +56,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<syntax>
<channel_snapshot/>
</syntax>
+ <see-also>
+ <ref type="managerEvent">Newchannel</ref>
+ <ref type="managerEvent">Hangup</ref>
+ </see-also>
</managerEventInstance>
</managerEvent>
<managerEvent language="en_US" name="Hangup">
@@ -66,6 +74,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>A description of why the channel was hung up.</para>
</parameter>
</syntax>
+ <see-also>
+ <ref type="managerEvent">Newchannel</ref>
+ <ref type="managerEvent">SoftHangupRequest</ref>
+ <ref type="managerEvent">HangupRequest</ref>
+ <ref type="managerEvent">Newstate</ref>
+ </see-also>
</managerEventInstance>
</managerEvent>
<managerEvent language="en_US" name="HangupRequest">
@@ -75,6 +89,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<channel_snapshot/>
<xi:include xpointer="xpointer(/docs/managerEvent[@name='Hangup']/managerEventInstance/syntax/parameter[@name='Cause'])" />
</syntax>
+ <see-also>
+ <ref type="managerEvent">SoftHangupRequest</ref>
+ <ref type="managerEvent">Hangup</ref>
+ </see-also>
</managerEventInstance>
</managerEvent>
<managerEvent language="en_US" name="SoftHangupRequest">
@@ -84,6 +102,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<channel_snapshot/>
<xi:include xpointer="xpointer(/docs/managerEvent[@name='Hangup']/managerEventInstance/syntax/parameter[@name='Cause'])" />
</syntax>
+ <see-also>
+ <ref type="managerEvent">HangupRequest</ref>
+ <ref type="managerEvent">Hangup</ref>
+ </see-also>
</managerEventInstance>
</managerEvent>
<managerEvent language="en_US" name="NewExten">
@@ -114,6 +136,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>A description of the Caller ID presentation.</para>
</parameter>
</syntax>
+ <see-also>
+ <ref type="function">CALLERID</ref>
+ </see-also>
</managerEventInstance>
</managerEvent>
<managerEvent language="en_US" name="NewAccountCode">
@@ -125,6 +150,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>The channel's previous account code</para>
</parameter>
</syntax>
+ <see-also>
+ <ref type="function">CHANNEL</ref>
+ </see-also>
</managerEventInstance>
</managerEvent>
<managerEvent language="en_US" name="DialBegin">
@@ -139,6 +167,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</syntax>
<see-also>
<ref type="application">Dial</ref>
+ <ref type="application">Originate</ref>
+ <ref type="manager">Originate</ref>
+ <ref type="managerEvent">DialEnd</ref>
</see-also>
</managerEventInstance>
</managerEvent>
@@ -192,6 +223,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</syntax>
<see-also>
<ref type="application">Dial</ref>
+ <ref type="application">Originate</ref>
+ <ref type="manager">Originate</ref>
+ <ref type="managerEvent">DialBegin</ref>
</see-also>
</managerEventInstance>
</managerEvent>
@@ -204,6 +238,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>The suggested MusicClass, if provided.</para>
</parameter>
</syntax>
+ <see-also>
+ <ref type="managerEvent">Unhold</ref>
+ </see-also>
</managerEventInstance>
</managerEvent>
<managerEvent language="en_US" name="Unhold">
@@ -212,6 +249,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<syntax>
<channel_snapshot/>
</syntax>
+ <see-also>
+ <ref type="managerEvent">Hold</ref>
+ </see-also>
</managerEventInstance>
</managerEvent>
<managerEvent language="en_US" name="ChanSpyStart">
@@ -222,7 +262,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<channel_snapshot prefix="Spyee"/>
</syntax>
<see-also>
- <ref type="application">ChanSpyStop</ref>
+ <ref type="managerEvent">ChanSpyStop</ref>
+ <ref type="application">ChanSpy</ref>
</see-also>
</managerEventInstance>
</managerEvent>
@@ -234,7 +275,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<channel_snapshot prefix="Spyee"/>
</syntax>
<see-also>
- <ref type="application">ChanSpyStart</ref>
+ <ref type="managerEvent">ChanSpyStart</ref>
+ <ref type="application">ChanSpy</ref>
</see-also>
</managerEventInstance>
</managerEvent>
@@ -247,6 +289,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>Hangup handler parameter string passed to the Gosub application.</para>
</parameter>
</syntax>
+ <see-also>
+ <ref type="function">CHANNEL</ref>
+ </see-also>
</managerEventInstance>
</managerEvent>
<managerEvent language="en_US" name="HangupHandlerPop">
@@ -353,6 +398,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</syntax>
<see-also>
<ref type="managerEvent">MusicOnHoldStop</ref>
+ <ref type="application">StartMusicOnHold</ref>
<ref type="application">MusicOnHold</ref>
</see-also>
</managerEventInstance>
@@ -829,6 +875,9 @@ static void channel_dtmf_begin_cb(void *data, struct stasis_subscription *sub,
</enumlist>
</parameter>
</syntax>
+ <see-also>
+ <ref type="managerEvent">DTMFEnd</ref>
+ </see-also>
</managerEventInstance>
***/
manager_event(EVENT_FLAG_DTMF, "DTMFBegin",
@@ -875,6 +924,9 @@ static void channel_dtmf_end_cb(void *data, struct stasis_subscription *sub,
</enumlist>
</parameter>
</syntax>
+ <see-also>
+ <ref type="managerEvent">DTMFBegin</ref>
+ </see-also>
</managerEventInstance>
***/
manager_event(EVENT_FLAG_DTMF, "DTMFEnd",
diff --git a/main/named_locks.c b/main/named_locks.c
index b977b553c..596048388 100644
--- a/main/named_locks.c
+++ b/main/named_locks.c
@@ -87,8 +87,8 @@ static void named_locks_shutdown(void)
int ast_named_locks_init(void)
{
- named_locks = ao2_container_alloc_hash(0, 0, NAMED_LOCKS_BUCKETS, named_locks_hash, NULL,
- named_locks_cmp);
+ named_locks = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0,
+ NAMED_LOCKS_BUCKETS, named_locks_hash, NULL, named_locks_cmp);
if (!named_locks) {
return -1;
}
diff --git a/main/pbx.c b/main/pbx.c
index 6b0069c06..3b9594a19 100644
--- a/main/pbx.c
+++ b/main/pbx.c
@@ -237,9 +237,11 @@ AST_THREADSTORAGE(extensionstate_buf);
priority.
*/
struct ast_exten {
- char *exten; /*!< Extension name */
+ char *exten; /*!< Clean Extension id */
+ char *name; /*!< Extension name (may include '-' eye candy) */
int matchcid; /*!< Match caller id ? */
const char *cidmatch; /*!< Caller id to match for this extension */
+ const char *cidmatch_display; /*!< Caller id to match (display version) */
int priority; /*!< Priority */
const char *label; /*!< Label */
struct ast_context *parent; /*!< The context this extension belongs to */
@@ -614,6 +616,7 @@ static int ast_add_extension2_lockopt(struct ast_context *con,
static struct ast_context *find_context_locked(const char *context);
static struct ast_context *find_context(const char *context);
static void get_device_state_causing_channels(struct ao2_container *c);
+static int ext_strncpy(char *dst, const char *src, int len, int nofluff);
/*!
* \internal
@@ -838,9 +841,13 @@ int check_contexts(char *file, int line )
e2 = ast_hashtab_lookup(c1->root_table, &ex);
if (!e2) {
if (e1->matchcid == AST_EXT_MATCHCID_ON) {
- ast_log(LOG_NOTICE,"Called from: %s:%d: The %s context records the exten %s (CID match: %s) but it is not in its root_table\n", file, line, c2->name, dummy_name, e1->cidmatch );
+ ast_log(LOG_NOTICE, "Called from: %s:%d: The %s context records "
+ "the exten %s (CID match: %s) but it is not in its root_table\n",
+ file, line, c2->name, dummy_name, e1->cidmatch_display);
} else {
- ast_log(LOG_NOTICE,"Called from: %s:%d: The %s context records the exten %s but it is not in its root_table\n", file, line, c2->name, dummy_name );
+ ast_log(LOG_NOTICE, "Called from: %s:%d: The %s context records "
+ "the exten %s but it is not in its root_table\n",
+ file, line, c2->name, dummy_name);
}
check_contexts_trouble();
}
@@ -1081,11 +1088,11 @@ static void cli_match_char_tree(struct match_char *node, char *prefix, int fd)
if (strlen(node->x) > 1) {
ast_cli(fd, "%s[%s]:%c:%c:%d:%s%s%s\n", prefix, node->x, node->is_pattern ? 'Y' : 'N',
node->deleted ? 'D' : '-', node->specificity, node->exten? "EXTEN:" : "",
- node->exten ? node->exten->exten : "", extenstr);
+ node->exten ? node->exten->name : "", extenstr);
} else {
ast_cli(fd, "%s%s:%c:%c:%d:%s%s%s\n", prefix, node->x, node->is_pattern ? 'Y' : 'N',
node->deleted ? 'D' : '-', node->specificity, node->exten? "EXTEN:" : "",
- node->exten ? node->exten->exten : "", extenstr);
+ node->exten ? node->exten->name : "", extenstr);
}
ast_str_set(&my_prefix, 0, "%s+ ", prefix);
@@ -1200,7 +1207,7 @@ static void new_find_extension(const char *str, struct scoreboard *score, struct
return; \
} \
} else { \
- ast_debug(4, "returning an exact match-- first found-- %s\n", p->exten->exten); \
+ ast_debug(4, "returning an exact match-- first found-- %s\n", p->exten->name); \
return; /* the first match, by definition, will be the best, because of the sorted tree */ \
} \
} \
@@ -1213,13 +1220,13 @@ static void new_find_extension(const char *str, struct scoreboard *score, struct
if (*(str + 1) || p->next_char->x[0] == '!') { \
new_find_extension(str + 1, score, p->next_char, length + 1, spec + p->specificity, callerid, label, action); \
if (score->exten) { \
- ast_debug(4 ,"returning an exact match-- %s\n", score->exten->exten); \
+ ast_debug(4 ,"returning an exact match-- %s\n", score->exten->name); \
return; /* the first match is all we need */ \
} \
} else { \
new_find_extension("/", score, p->next_char, length + 1, spec + p->specificity, callerid, label, action); \
if (score->exten || ((action == E_CANMATCH || action == E_MATCHMORE) && score->canmatch)) { \
- ast_debug(4,"returning a (can/more) match--- %s\n", score->exten ? score->exten->exten : \
+ ast_debug(4,"returning a (can/more) match--- %s\n", score->exten ? score->exten->name : \
"NULL"); \
return; /* the first match is all we need */ \
} \
@@ -1257,14 +1264,17 @@ static void new_find_extension(const char *str, struct scoreboard *score, struct
if (p->exten && *str2 != '/') {
update_scoreboard(score, length + i, spec + (i * p->specificity), p->exten, '.', callerid, p->deleted, p);
if (score->exten) {
- ast_debug(4,"return because scoreboard has a match with '/'--- %s\n", score->exten->exten);
+ ast_debug(4, "return because scoreboard has a match with '/'--- %s\n",
+ score->exten->name);
return; /* the first match is all we need */
}
}
if (p->next_char && p->next_char->x[0] == '/' && p->next_char->x[1] == 0) {
new_find_extension("/", score, p->next_char, length + i, spec+(p->specificity*i), callerid, label, action);
if (score->exten || ((action == E_CANMATCH || action == E_MATCHMORE) && score->canmatch)) {
- ast_debug(4, "return because scoreboard has exact match OR CANMATCH/MATCHMORE & canmatch set--- %s\n", score->exten ? score->exten->exten : "NULL");
+ ast_debug(4, "return because scoreboard has exact match OR "
+ "CANMATCH/MATCHMORE & canmatch set--- %s\n",
+ score->exten ? score->exten->name : "NULL");
return; /* the first match is all we need */
}
}
@@ -1279,14 +1289,17 @@ static void new_find_extension(const char *str, struct scoreboard *score, struct
if (p->exten && *str2 != '/') {
update_scoreboard(score, length + 1, spec + (p->specificity * i), p->exten, '!', callerid, p->deleted, p);
if (score->exten) {
- ast_debug(4, "return because scoreboard has a '!' match--- %s\n", score->exten->exten);
+ ast_debug(4, "return because scoreboard has a '!' match--- %s\n",
+ score->exten->name);
return; /* the first match is all we need */
}
}
if (p->next_char && p->next_char->x[0] == '/' && p->next_char->x[1] == 0) {
new_find_extension("/", score, p->next_char, length + i, spec + (p->specificity * i), callerid, label, action);
if (score->exten || ((action == E_CANMATCH || action == E_MATCHMORE) && score->canmatch)) {
- ast_debug(4, "return because scoreboard has exact match OR CANMATCH/MATCHMORE & canmatch set with '/' and '!'--- %s\n", score->exten ? score->exten->exten : "NULL");
+ ast_debug(4, "return because scoreboard has exact match OR "
+ "CANMATCH/MATCHMORE & canmatch set with '/' and '!'--- %s\n",
+ score->exten ? score->exten->name : "NULL");
return; /* the first match is all we need */
}
}
@@ -1295,7 +1308,9 @@ static void new_find_extension(const char *str, struct scoreboard *score, struct
if (p->next_char && callerid && *callerid) {
new_find_extension(callerid, score, p->next_char, length + 1, spec, callerid, label, action);
if (score->exten || ((action == E_CANMATCH || action == E_MATCHMORE) && score->canmatch)) {
- ast_debug(4, "return because scoreboard has exact match OR CANMATCH/MATCHMORE & canmatch set with '/'--- %s\n", score->exten ? score->exten->exten : "NULL");
+ ast_debug(4, "return because scoreboard has exact match OR "
+ "CANMATCH/MATCHMORE & canmatch set with '/'--- %s\n",
+ score->exten ? score->exten->name : "NULL");
return; /* the first match is all we need */
}
}
@@ -1648,7 +1663,7 @@ static struct match_char *add_exten_to_pattern_tree(struct ast_context *con, str
}
if (m2->exten) {
ast_log(LOG_WARNING, "Found duplicate exten. Had %s found %s\n",
- m2->deleted ? "(deleted/invalid)" : m2->exten->exten, e1->exten);
+ m2->deleted ? "(deleted/invalid)" : m2->exten->name, e1->name);
}
m2->exten = e1;
m2->deleted = 0;
@@ -1674,7 +1689,7 @@ static struct match_char *add_exten_to_pattern_tree(struct ast_context *con, str
if (!pat_node[idx_next].buf[0]) {
if (m2 && m2->exten) {
ast_log(LOG_WARNING, "Found duplicate exten. Had %s found %s\n",
- m2->deleted ? "(deleted/invalid)" : m2->exten->exten, e1->exten);
+ m2->deleted ? "(deleted/invalid)" : m2->exten->name, e1->name);
}
m1->deleted = 0;
m1->exten = e1;
@@ -2081,6 +2096,41 @@ static int ext_cmp(const char *left, const char *right)
return ext_cmp_pattern(left + 1, right + 1);
}
+static int ext_fluff_count(const char *exten)
+{
+ int fluff = 0;
+
+ if (*exten != '_') {
+ /* not a pattern, simple check. */
+ while (*exten) {
+ if (*exten == '-') {
+ fluff++;
+ }
+ exten++;
+ }
+
+ return fluff;
+ }
+
+ /* do pattern check */
+ while (*exten) {
+ if (*exten == '-') {
+ fluff++;
+ } else if (*exten == '[') {
+ /* skip set, dashes here matter. */
+ exten = strchr(exten, ']');
+
+ if (!exten) {
+ /* we'll end up warning about this later, don't spam logs */
+ return fluff;
+ }
+ }
+ exten++;
+ }
+
+ return fluff;
+}
+
int ast_extension_cmp(const char *a, const char *b)
{
int cmp;
@@ -4682,6 +4732,13 @@ static struct ast_context *find_context_locked(const char *context)
return c;
}
+/*! \brief Free an ast_include and associated data. */
+static void include_free(struct ast_include *include)
+{
+ ast_destroy_timing(&(include->timing));
+ ast_free(include);
+}
+
/*!
* \brief Remove included contexts.
* This function locks contexts list by &conlist, search for the right context
@@ -4729,8 +4786,7 @@ int ast_context_remove_include2(struct ast_context *con, const char *include, co
else
con->includes = i->next;
/* free include and return */
- ast_destroy_timing(&(i->timing));
- ast_free(i);
+ include_free(i);
ret = 0;
break;
}
@@ -4836,6 +4892,7 @@ int ast_context_remove_extension_callerid2(struct ast_context *con, const char *
struct ast_exten *peer;
struct ast_exten ex, *exten2, *exten3;
char dummy_name[1024];
+ char dummy_cid[1024];
struct ast_exten *previous_peer = NULL;
struct ast_exten *next_peer = NULL;
int found = 0;
@@ -4851,9 +4908,14 @@ int ast_context_remove_extension_callerid2(struct ast_context *con, const char *
#endif
/* find this particular extension */
ex.exten = dummy_name;
+ ext_strncpy(dummy_name, extension, sizeof(dummy_name), 1);
ex.matchcid = matchcallerid;
- ex.cidmatch = callerid;
- ast_copy_string(dummy_name, extension, sizeof(dummy_name));
+ if (callerid) {
+ ex.cidmatch = dummy_cid;
+ ext_strncpy(dummy_cid, callerid, sizeof(dummy_cid), 1);
+ } else {
+ ex.cidmatch = NULL;
+ }
exten = ast_hashtab_lookup(con->root_table, &ex);
if (exten) {
if (priority == 0) {
@@ -4876,13 +4938,19 @@ int ast_context_remove_extension_callerid2(struct ast_context *con, const char *
if (exten2) {
if (exten2->label) { /* if this exten has a label, remove that, too */
exten3 = ast_hashtab_remove_this_object(exten->peer_label_table,exten2);
- if (!exten3)
- ast_log(LOG_ERROR,"Did not remove this priority label (%d/%s) from the peer_label_table of context %s, extension %s!\n", priority, exten2->label, con->name, exten2->exten);
+ if (!exten3) {
+ ast_log(LOG_ERROR, "Did not remove this priority label (%d/%s) "
+ "from the peer_label_table of context %s, extension %s!\n",
+ priority, exten2->label, con->name, exten2->name);
+ }
}
exten3 = ast_hashtab_remove_this_object(exten->peer_table, exten2);
- if (!exten3)
- ast_log(LOG_ERROR,"Did not remove this priority (%d) from the peer_table of context %s, extension %s!\n", priority, con->name, exten2->exten);
+ if (!exten3) {
+ ast_log(LOG_ERROR, "Did not remove this priority (%d) from the "
+ "peer_table of context %s, extension %s!\n",
+ priority, con->name, exten2->name);
+ }
if (exten2 == exten && exten2->peer) {
exten2 = ast_hashtab_remove_this_object(con->root_table, exten);
ast_hashtab_insert_immediate(con->root_table, exten2->peer);
@@ -4891,8 +4959,11 @@ int ast_context_remove_extension_callerid2(struct ast_context *con, const char *
/* well, if the last priority of an exten is to be removed,
then, the extension is removed, too! */
exten3 = ast_hashtab_remove_this_object(con->root_table, exten);
- if (!exten3)
- ast_log(LOG_ERROR,"Did not remove this exten (%s) from the context root_table (%s) (priority %d)\n", exten->exten, con->name, priority);
+ if (!exten3) {
+ ast_log(LOG_ERROR, "Did not remove this exten (%s) from the "
+ "context root_table (%s) (priority %d)\n",
+ exten->name, con->name, priority);
+ }
if (con->pattern_tree) {
struct match_char *x = add_exten_to_pattern_tree(con, exten, 1);
if (x->exten) { /* this test for safety purposes */
@@ -4903,7 +4974,7 @@ int ast_context_remove_extension_callerid2(struct ast_context *con, const char *
}
} else {
ast_log(LOG_ERROR,"Could not find priority %d of exten %s in context %s!\n",
- priority, exten->exten, con->name);
+ priority, exten->name, con->name);
}
}
} else {
@@ -4920,10 +4991,12 @@ int ast_context_remove_extension_callerid2(struct ast_context *con, const char *
/* scan the extension list to find first matching extension-registrar */
for (exten = con->root; exten; prev_exten = exten, exten = exten->next) {
- if (!strcmp(exten->exten, extension) &&
- (!registrar || !strcmp(exten->registrar, registrar)) &&
- (!matchcallerid || (!ast_strlen_zero(callerid) && !ast_strlen_zero(exten->cidmatch) && !strcmp(exten->cidmatch, callerid)) || (ast_strlen_zero(callerid) && ast_strlen_zero(exten->cidmatch))))
+ if (!strcmp(exten->exten, ex.exten) &&
+ (!matchcallerid ||
+ (!ast_strlen_zero(ex.cidmatch) && !ast_strlen_zero(exten->cidmatch) && !strcmp(exten->cidmatch, ex.cidmatch)) ||
+ (ast_strlen_zero(ex.cidmatch) && ast_strlen_zero(exten->cidmatch)))) {
break;
+ }
}
if (!exten) {
/* we can't find right extension */
@@ -4934,8 +5007,8 @@ int ast_context_remove_extension_callerid2(struct ast_context *con, const char *
/* scan the priority list to remove extension with exten->priority == priority */
for (peer = exten, next_peer = exten->peer ? exten->peer : exten->next;
- peer && !strcmp(peer->exten, extension) &&
- (!callerid || (!matchcallerid && !peer->matchcid) || (matchcallerid && peer->matchcid && !strcmp(peer->cidmatch, callerid))) ;
+ peer && !strcmp(peer->exten, ex.exten) &&
+ (!callerid || (!matchcallerid && !peer->matchcid) || (matchcallerid && peer->matchcid && !strcmp(peer->cidmatch, ex.cidmatch))) ;
peer = next_peer, next_peer = next_peer ? (next_peer->peer ? next_peer->peer : next_peer->next) : NULL) {
if ((priority == 0 || peer->priority == priority) &&
@@ -6164,11 +6237,11 @@ 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,
+ res1 = ast_add_extension2(new, 0, prio_item->name, prio_item->priority, prio_item->label,
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);
+ context->name, prio_item->name, prio_item->priority, prio_item->app, (char*)prio_item->data, prio_item->registrar);
} else {
/* we do NOT pass the priority data from the old to the new -- we pass a copy of it, so no changes to the current dialplan take place,
and no double frees take place, either! */
@@ -6481,8 +6554,7 @@ int ast_context_add_include2(struct ast_context *con, const char *value,
/* ... go to last include and check if context is already included too... */
for (i = con->includes; i; i = i->next) {
if (!strcasecmp(i->name, new_include->name)) {
- ast_destroy_timing(&(new_include->timing));
- ast_free(new_include);
+ include_free(new_include);
ast_unlock_context(con);
errno = EEXIST;
return -1;
@@ -6817,8 +6889,8 @@ int ast_async_goto_by_name(const char *channame, const char *context, const char
return res;
}
-/*! \brief copy a string skipping whitespace */
-static int ext_strncpy(char *dst, const char *src, int len)
+/*! \brief copy a string skipping whitespace and dashes */
+static int ext_strncpy(char *dst, const char *src, int len, int nofluff)
{
int count = 0;
int insquares = 0;
@@ -6831,6 +6903,9 @@ static int ext_strncpy(char *dst, const char *src, int len)
} else if (*src == ' ' && !insquares) {
src++;
continue;
+ } else if (*src == '-' && !insquares && nofluff) {
+ src++;
+ continue;
}
*dst = *src;
dst++;
@@ -6856,14 +6931,14 @@ static int add_priority(struct ast_context *con, struct ast_exten *tmp,
for (ep = NULL; e ; ep = e, e = e->peer) {
if (e->label && tmp->label && e->priority != tmp->priority && !strcmp(e->label, tmp->label)) {
- if (strcmp(e->exten, tmp->exten)) {
+ if (strcmp(e->name, tmp->name)) {
ast_log(LOG_WARNING,
"Extension '%s' priority %d in '%s', label '%s' already in use at aliased extension '%s' priority %d\n",
- tmp->exten, tmp->priority, con->name, tmp->label, e->exten, e->priority);
+ tmp->name, tmp->priority, con->name, tmp->label, e->name, e->priority);
} else {
ast_log(LOG_WARNING,
"Extension '%s' priority %d in '%s', label '%s' already in use at priority %d\n",
- tmp->exten, tmp->priority, con->name, tmp->label, e->priority);
+ tmp->name, tmp->priority, con->name, tmp->label, e->priority);
}
repeated_label = 1;
}
@@ -6889,14 +6964,14 @@ static int add_priority(struct ast_context *con, struct ast_exten *tmp,
/* Can't have something exactly the same. Is this a
replacement? If so, replace, otherwise, bonk. */
if (!replace) {
- if (strcmp(e->exten, tmp->exten)) {
+ if (strcmp(e->name, tmp->name)) {
ast_log(LOG_WARNING,
"Unable to register extension '%s' priority %d in '%s', already in use by aliased extension '%s'\n",
- tmp->exten, tmp->priority, con->name, e->exten);
+ tmp->name, tmp->priority, con->name, e->name);
} else {
ast_log(LOG_WARNING,
"Unable to register extension '%s' priority %d in '%s', already in use\n",
- tmp->exten, tmp->priority, con->name);
+ tmp->name, tmp->priority, con->name);
}
return -1;
@@ -7081,6 +7156,8 @@ static int ast_add_extension2_lockopt(struct ast_context *con,
char expand_buf[VAR_BUF_SIZE];
struct ast_exten dummy_exten = {0};
char dummy_name[1024];
+ int exten_fluff;
+ int callerid_fluff;
if (ast_strlen_zero(extension)) {
ast_log(LOG_ERROR,"You have to be kidding-- add exten '' to context %s? Figure out a name and call me back. Action ignored.\n",
@@ -7090,28 +7167,51 @@ static int ast_add_extension2_lockopt(struct ast_context *con,
/* If we are adding a hint evalulate in variables and global variables */
if (priority == PRIORITY_HINT && strstr(application, "${") && extension[0] != '_') {
+ int inhibited;
struct ast_channel *c = ast_dummy_channel_alloc();
if (c) {
ast_channel_exten_set(c, extension);
ast_channel_context_set(c, con->name);
}
+
+ /*
+ * We can allow dangerous functions when adding a hint since
+ * altering dialplan is itself a privileged activity. Otherwise,
+ * we could never execute dangerous functions.
+ */
+ inhibited = ast_thread_inhibit_escalations_swap(0);
pbx_substitute_variables_helper(c, application, expand_buf, sizeof(expand_buf));
+ if (0 < inhibited) {
+ ast_thread_inhibit_escalations();
+ }
+
application = expand_buf;
if (c) {
ast_channel_unref(c);
}
}
+ exten_fluff = ext_fluff_count(extension);
+ callerid_fluff = callerid ? ext_fluff_count(callerid) : 0;
+
length = sizeof(struct ast_exten);
length += strlen(extension) + 1;
+ if (exten_fluff) {
+ length += strlen(extension) + 1 - exten_fluff;
+ }
length += strlen(application) + 1;
- if (label)
+ if (label) {
length += strlen(label) + 1;
- if (callerid)
+ }
+ if (callerid) {
length += strlen(callerid) + 1;
- else
+ if (callerid_fluff) {
+ length += strlen(callerid) + 1 - callerid_fluff;
+ }
+ } else {
length ++; /* just the '\0' */
+ }
/* Be optimistic: Build the extension structure first */
if (!(tmp = ast_calloc(1, length)))
@@ -7127,14 +7227,25 @@ static int ast_add_extension2_lockopt(struct ast_context *con,
strcpy(p, label);
p += strlen(label) + 1;
}
- tmp->exten = p;
- p += ext_strncpy(p, extension, strlen(extension) + 1) + 1;
+ tmp->name = p;
+ p += ext_strncpy(p, extension, strlen(extension) + 1, 0) + 1;
+ if (exten_fluff) {
+ tmp->exten = p;
+ p += ext_strncpy(p, extension, strlen(extension) + 1, 1) + 1;
+ } else {
+ /* no fluff, we don't need a copy. */
+ tmp->exten = tmp->name;
+ }
tmp->priority = priority;
- tmp->cidmatch = p; /* but use p for assignments below */
+ tmp->cidmatch_display = tmp->cidmatch = p; /* but use p for assignments below */
/* Blank callerid and NULL callerid are two SEPARATE things. Do NOT confuse the two!!! */
if (callerid) {
- p += ext_strncpy(p, callerid, strlen(callerid) + 1) + 1;
+ p += ext_strncpy(p, callerid, strlen(callerid) + 1, 0) + 1;
+ if (callerid_fluff) {
+ tmp->cidmatch = p;
+ p += ext_strncpy(p, callerid, strlen(callerid) + 1, 1) + 1;
+ }
tmp->matchcid = AST_EXT_MATCHCID_ON;
} else {
*p++ = '\0';
@@ -7153,7 +7264,7 @@ static int ast_add_extension2_lockopt(struct ast_context *con,
if (con->pattern_tree) { /* usually, on initial load, the pattern_tree isn't formed until the first find_exten; so if we are adding
an extension, and the trie exists, then we need to incrementally add this pattern to it. */
- ast_copy_string(dummy_name, extension, sizeof(dummy_name));
+ ext_strncpy(dummy_name, tmp->exten, sizeof(dummy_name), 1);
dummy_exten.exten = dummy_name;
dummy_exten.matchcid = AST_EXT_MATCHCID_OFF;
dummy_exten.cidmatch = 0;
@@ -7271,19 +7382,19 @@ static int ast_add_extension2_lockopt(struct ast_context *con,
if (option_debug) {
if (tmp->matchcid == AST_EXT_MATCHCID_ON) {
ast_debug(1, "Added extension '%s' priority %d (CID match '%s') to %s (%p)\n",
- tmp->exten, tmp->priority, tmp->cidmatch, con->name, con);
+ tmp->name, tmp->priority, tmp->cidmatch_display, con->name, con);
} else {
ast_debug(1, "Added extension '%s' priority %d to %s (%p)\n",
- tmp->exten, tmp->priority, con->name, con);
+ tmp->name, tmp->priority, con->name, con);
}
}
if (tmp->matchcid == AST_EXT_MATCHCID_ON) {
ast_verb(3, "Added extension '%s' priority %d (CID match '%s') to %s\n",
- tmp->exten, tmp->priority, tmp->cidmatch, con->name);
+ tmp->name, tmp->priority, tmp->cidmatch_display, con->name);
} else {
ast_verb(3, "Added extension '%s' priority %d to %s\n",
- tmp->exten, tmp->priority, con->name);
+ tmp->name, tmp->priority, con->name);
}
return 0;
@@ -7706,7 +7817,7 @@ static void __ast_internal_context_destroy( struct ast_context *con)
for (tmpi = tmp->includes; tmpi; ) { /* Free includes */
struct ast_include *tmpil = tmpi;
tmpi = tmpi->next;
- ast_free(tmpil);
+ include_free(tmpil);
}
for (ipi = tmp->ignorepats; ipi; ) { /* Free ignorepats */
struct ast_ignorepat *ipl = ipi;
@@ -7800,12 +7911,12 @@ void __ast_context_destroy(struct ast_context *list, struct ast_hashtab *context
if (pi) {
pi->next = i->next;
/* free include */
- ast_free(i);
+ include_free(i);
continue; /* don't change pi */
} else {
tmp->includes = i->next;
/* free include */
- ast_free(i);
+ include_free(i);
continue; /* don't change pi */
}
}
@@ -7842,7 +7953,7 @@ void __ast_context_destroy(struct ast_context *list, struct ast_hashtab *context
continue;
}
ast_verb(3, "Remove %s/%s/%d, registrar=%s; con=%s(%p); con->root=%p\n",
- tmp->name, prio_item->exten, prio_item->priority, registrar, con? con->name : "<nil>", con, con? con->root_table: NULL);
+ tmp->name, prio_item->name, prio_item->priority, registrar, con? con->name : "<nil>", con, con? con->root_table: NULL);
ast_copy_string(extension, prio_item->exten, sizeof(extension));
if (prio_item->cidmatch) {
ast_copy_string(cidmatch, prio_item->cidmatch, sizeof(cidmatch));
@@ -8242,7 +8353,7 @@ struct ast_context *ast_get_extension_context(struct ast_exten *exten)
const char *ast_get_extension_name(struct ast_exten *exten)
{
- return exten ? exten->exten : NULL;
+ return exten ? exten->name : NULL;
}
const char *ast_get_extension_label(struct ast_exten *exten)
@@ -8295,7 +8406,7 @@ int ast_get_extension_matchcid(struct ast_exten *e)
const char *ast_get_extension_cidmatch(struct ast_exten *e)
{
- return e ? e->cidmatch : NULL;
+ return e ? e->cidmatch_display : NULL;
}
const char *ast_get_extension_app(struct ast_exten *e)
diff --git a/main/pbx_functions.c b/main/pbx_functions.c
index bc738b043..558be461f 100644
--- a/main/pbx_functions.c
+++ b/main/pbx_functions.c
@@ -482,7 +482,6 @@ int ast_thread_inhibit_escalations(void)
thread_inhibit_escalations = ast_threadstorage_get(
&thread_inhibit_escalations_tl, sizeof(*thread_inhibit_escalations));
-
if (thread_inhibit_escalations == NULL) {
ast_log(LOG_ERROR, "Error inhibiting privilege escalations for current thread\n");
return -1;
@@ -492,6 +491,23 @@ int ast_thread_inhibit_escalations(void)
return 0;
}
+int ast_thread_inhibit_escalations_swap(int inhibit)
+{
+ int *thread_inhibit_escalations;
+ int orig;
+
+ thread_inhibit_escalations = ast_threadstorage_get(
+ &thread_inhibit_escalations_tl, sizeof(*thread_inhibit_escalations));
+ if (thread_inhibit_escalations == NULL) {
+ ast_log(LOG_ERROR, "Error swapping privilege escalations inhibit for current thread\n");
+ return -1;
+ }
+
+ orig = *thread_inhibit_escalations;
+ *thread_inhibit_escalations = !!inhibit;
+ return orig;
+}
+
/*!
* \brief Indicates whether the current thread inhibits the execution of
* dangerous functions.
@@ -505,7 +521,6 @@ static int thread_inhibits_escalations(void)
thread_inhibit_escalations = ast_threadstorage_get(
&thread_inhibit_escalations_tl, sizeof(*thread_inhibit_escalations));
-
if (thread_inhibit_escalations == NULL) {
ast_log(LOG_ERROR, "Error checking thread's ability to run dangerous functions\n");
/* On error, assume that we are inhibiting */
diff --git a/main/rtp_engine.c b/main/rtp_engine.c
index 462d4c530..7a8378346 100644
--- a/main/rtp_engine.c
+++ b/main/rtp_engine.c
@@ -102,6 +102,9 @@
and sending this report.</para>
</parameter>
</syntax>
+ <see-also>
+ <ref type="managerEvent">RTCPReceived</ref>
+ </see-also>
</managerEventInstance>
</managerEvent>
<managerEvent language="en_US" name="RTCPReceived">
@@ -131,6 +134,9 @@
<xi:include xpointer="xpointer(/docs/managerEvent[@name='RTCPSent']/managerEventInstance/syntax/parameter[@name='SentOctets'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='RTCPSent']/managerEventInstance/syntax/parameter[contains(@name, 'ReportX')])" />
</syntax>
+ <see-also>
+ <ref type="managerEvent">RTCPSent</ref>
+ </see-also>
</managerEventInstance>
</managerEvent>
***/
@@ -737,6 +743,7 @@ int ast_rtp_codecs_payloads_set_rtpmap_type_rate(struct ast_rtp_codecs *codecs,
} else {
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, "");
@@ -1773,7 +1780,11 @@ 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));
+ /*
+ * ARRAY_LEN's result is cast to an int so 'map' is not autocast to a size_t,
+ * which if negative would cause an assertion.
+ */
+ ast_assert(map < (int)ARRAY_LEN(static_RTP_PT));
ast_rwlock_wrlock(&static_RTP_PT_lock);
if (map < 0) {
@@ -1784,6 +1795,7 @@ static void add_static_payload(int map, struct ast_format *format, int rtp_code)
break;
}
}
+
if (map < 0) {
if (format) {
ast_log(LOG_WARNING, "No Dynamic RTP mapping available for format %s\n",
@@ -1815,14 +1827,10 @@ static void add_static_payload(int map, struct ast_format *format, int rtp_code)
int ast_rtp_engine_load_format(struct ast_format *format)
{
- char *codec_name = ast_strdupa(ast_format_get_name(format));
-
- codec_name = ast_str_to_upper(codec_name);
-
set_next_mime_type(format,
0,
ast_codec_media_type2str(ast_format_get_type(format)),
- codec_name,
+ ast_format_get_codec_name(format),
ast_format_get_sample_rate(format));
add_static_payload(-1, format, 0);
diff --git a/main/say.c b/main/say.c
index ef80dfa7d..f19ed71a1 100644
--- a/main/say.c
+++ b/main/say.c
@@ -5201,13 +5201,14 @@ int ast_say_date_with_format_it(struct ast_channel *chan, time_t t, const char *
case 'I':
case 'l':
/* 12-Hour */
- if (tm.tm_hour == 0)
+ if (tm.tm_hour == 0) {
ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
- else if (tm.tm_hour > 12)
+ } else if (tm.tm_hour > 12) {
snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
- else
+ } else {
snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
- res = wait_file(chan, ints, nextmsg, lang);
+ }
+ res = wait_file(chan, ints, nextmsg, lang);
break;
case 'H':
case 'k':
@@ -5227,11 +5228,12 @@ int ast_say_date_with_format_it(struct ast_channel *chan, time_t t, const char *
case 'P':
case 'p':
/* AM/PM */
- if (tm.tm_hour > 11)
+ if (tm.tm_hour > 11) {
ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
- else
+ } else {
ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
- res = wait_file(chan, ints, nextmsg, lang);
+ }
+ res = wait_file(chan, ints, nextmsg, lang);
break;
case 'Q':
/* Shorthand for "Today", "Yesterday", or ABdY */
@@ -7948,9 +7950,9 @@ int ast_say_date_with_format_ja(struct ast_channel *chan, time_t time, const cha
/* NOTE: if you add more options here, please try to be consistent with strftime(3) */
case '\'':
/* Literal name of a sound file */
- sndoffset=0;
- for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
+ for (sndoffset = 0 ; (format[++offset] != '\'') && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
sndfile[sndoffset] = format[offset];
+ }
sndfile[sndoffset] = '\0';
res = wait_file(chan,ints,sndfile,lang);
break;
diff --git a/main/sorcery.c b/main/sorcery.c
index 4dedc4d9b..bfc768879 100644
--- a/main/sorcery.c
+++ b/main/sorcery.c
@@ -290,6 +290,12 @@ static int bool_handler_fn(const void *obj, const intptr_t *args, char **buf)
return !(*buf = ast_strdup(*field ? "true" : "false")) ? -1 : 0;
}
+static int yesno_handler_fn(const void *obj, const intptr_t *args, char **buf)
+{
+ unsigned int *field = (unsigned int *)(obj + args[0]);
+ return !(*buf = ast_strdup(*field ? "yes" : "no")) ? -1 : 0;
+}
+
static int sockaddr_handler_fn(const void *obj, const intptr_t *args, char **buf)
{
struct ast_sockaddr *field = (struct ast_sockaddr *)(obj + args[0]);
@@ -313,6 +319,7 @@ static sorcery_field_handler sorcery_field_default_handler(enum aco_option_type
{
switch(type) {
case OPT_BOOL_T: return bool_handler_fn;
+ case OPT_YESNO_T: return yesno_handler_fn;
case OPT_CHAR_ARRAY_T: return chararray_handler_fn;
case OPT_CODEC_T: return codec_handler_fn;
case OPT_DOUBLE_T: return double_handler_fn;
diff --git a/main/stasis.c b/main/stasis.c
index bbafb69e1..48e4eb5a5 100644
--- a/main/stasis.c
+++ b/main/stasis.c
@@ -59,6 +59,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
</description>
<see-also>
<ref type="application">UserEvent</ref>
+ <ref type="managerEvent">UserEvent</ref>
</see-also>
</managerEventInstance>
</managerEvent>
diff --git a/main/stasis_bridges.c b/main/stasis_bridges.c
index 183855773..3688a46ad 100644
--- a/main/stasis_bridges.c
+++ b/main/stasis_bridges.c
@@ -79,6 +79,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>Destination extension for the blind transfer.</para>
</parameter>
</syntax>
+ <see-also>
+ <ref type="manager">BlindTransfer</ref>
+ </see-also>
</managerEventInstance>
</managerEvent>
<managerEvent language="en_US" name="AttendedTransfer">
@@ -131,6 +134,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para> <replaceable>SecondTransfererChannel</replaceable>: Alice's channel in the bridge with Bob.</para>
<para> <replaceable>SecondBridgeUniqueid</replaceable>: The bridge between Alice and Bob.</para>
</description>
+ <see-also>
+ <ref type="manager">AtxFer</ref>
+ </see-also>
</managerEventInstance>
</managerEvent>
***/
diff --git a/main/stasis_endpoints.c b/main/stasis_endpoints.c
index c31714bf4..8cc60506c 100644
--- a/main/stasis_endpoints.c
+++ b/main/stasis_endpoints.c
@@ -253,6 +253,7 @@ static struct ast_json *contactstatus_to_json(struct stasis_message *msg, const
struct ast_endpoint_blob *obj = stasis_message_data(msg);
struct ast_json *json_endpoint;
struct ast_json *json_final;
+ const char *rtt;
const struct timeval *tv = stasis_message_timestamp(msg);
json_endpoint = ast_endpoint_snapshot_to_json(obj->snapshot, NULL);
@@ -260,15 +261,30 @@ static struct ast_json *contactstatus_to_json(struct stasis_message *msg, const
return NULL;
}
- json_final = ast_json_pack("{s: s, s: o, s: o, s: { s: s, s: s, s: s, 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")));
+ /* The roundtrip time is optional. */
+ rtt = ast_json_string_get(ast_json_object_get(obj->blob, "roundtrip_usec"));
+ if (!ast_strlen_zero(rtt)) {
+ 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", rtt);
+ } else {
+ json_final = ast_json_pack("{s: s, s: o, s: o, 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")));
+ }
if (!json_final) {
ast_json_unref(json_endpoint);
}
diff --git a/main/stasis_system.c b/main/stasis_system.c
index e232b8e8a..67970bd74 100644
--- a/main/stasis_system.c
+++ b/main/stasis_system.c
@@ -115,6 +115,7 @@ STASIS_MESSAGE_TYPE_DEFN(ast_cc_failure_type,
STASIS_MESSAGE_TYPE_DEFN(ast_cc_monitorfailed_type,
.to_ami = cc_monitorfailed_to_ami,
);
+STASIS_MESSAGE_TYPE_DEFN(ast_cluster_discovery_type);
void ast_system_publish_registry(const char *channeltype, const char *username, const char *domain, const char *status, const char *cause)
{
@@ -362,6 +363,7 @@ static void stasis_system_cleanup(void)
STASIS_MESSAGE_TYPE_CLEANUP(ast_cc_recallcomplete_type);
STASIS_MESSAGE_TYPE_CLEANUP(ast_cc_failure_type);
STASIS_MESSAGE_TYPE_CLEANUP(ast_cc_monitorfailed_type);
+ STASIS_MESSAGE_TYPE_CLEANUP(ast_cluster_discovery_type);
}
/*! \brief Initialize the system level items for \ref stasis */
@@ -422,5 +424,9 @@ int ast_stasis_system_init(void)
return -1;
}
+ if (STASIS_MESSAGE_TYPE_INIT(ast_cluster_discovery_type) != 0) {
+ return -1;
+ }
+
return 0;
}
diff --git a/main/stdtime/localtime.c b/main/stdtime/localtime.c
index 702edbe85..9cdf614d5 100644
--- a/main/stdtime/localtime.c
+++ b/main/stdtime/localtime.c
@@ -1849,13 +1849,14 @@ void ast_get_dst_info(const time_t * const timep, int *dst_enabled, time_t *dst_
*dst_enabled = 0;
/* Find where I can get gmtoff */
i = 0;
- while (sp->ttis[i].tt_isdst)
+ while (sp->ttis[i].tt_isdst) {
if (++i >= sp->typecnt) {
- i = 0;
- break;
+ i = 0;
+ break;
}
- *gmt_off = sp->ttis[i].tt_gmtoff;
- return;
+ }
+ *gmt_off = sp->ttis[i].tt_gmtoff;
+ return;
}
for (i = 1; i < sp->timecnt; ++i) {
diff --git a/main/taskprocessor.c b/main/taskprocessor.c
index 7ce3e4f16..bbf282c27 100644
--- a/main/taskprocessor.c
+++ b/main/taskprocessor.c
@@ -91,6 +91,8 @@ struct ast_taskprocessor {
unsigned int high_water_warned:1;
/*! Indicates that a high water alert is active on this taskprocessor */
unsigned int high_water_alert:1;
+ /*! Indicates if the taskprocessor is currently suspended */
+ unsigned int suspended:1;
};
/*!
@@ -606,7 +608,7 @@ int ast_taskprocessor_alert_set_levels(struct ast_taskprocessor *tps, long low_w
tps_alert_add(tps, -1);
}
} else {
- if (high_water <= tps->tps_queue_size) {
+ if (high_water < tps->tps_queue_size) {
/* Update water mark alert immediately */
tps->high_water_alert = 1;
tps_alert_add(tps, +1);
@@ -881,11 +883,11 @@ static int taskprocessor_push(struct ast_taskprocessor *tps, struct tps_task *t)
AST_LIST_INSERT_TAIL(&tps->tps_queue, t, list);
previous_size = tps->tps_queue_size++;
- if (previous_size >= tps->tps_queue_high) {
+ if (tps->tps_queue_high <= tps->tps_queue_size) {
if (!tps->high_water_warned) {
tps->high_water_warned = 1;
- ast_log(LOG_WARNING, "The '%s' task processor queue reached %d scheduled tasks.\n",
- tps->name, previous_size);
+ ast_log(LOG_WARNING, "The '%s' task processor queue reached %ld scheduled tasks.\n",
+ tps->name, tps->tps_queue_size);
}
if (!tps->high_water_alert) {
tps->high_water_alert = 1;
@@ -910,6 +912,33 @@ int ast_taskprocessor_push_local(struct ast_taskprocessor *tps, int (*task_exe)(
return taskprocessor_push(tps, tps_task_alloc_local(task_exe, datap));
}
+int ast_taskprocessor_suspend(struct ast_taskprocessor *tps)
+{
+ if (tps) {
+ ao2_lock(tps);
+ tps->suspended = 1;
+ ao2_unlock(tps);
+ return 0;
+ }
+ return -1;
+}
+
+int ast_taskprocessor_unsuspend(struct ast_taskprocessor *tps)
+{
+ if (tps) {
+ ao2_lock(tps);
+ tps->suspended = 0;
+ ao2_unlock(tps);
+ return 0;
+ }
+ return -1;
+}
+
+int ast_taskprocessor_is_suspended(struct ast_taskprocessor *tps)
+{
+ return tps ? tps->suspended : -1;
+}
+
int ast_taskprocessor_execute(struct ast_taskprocessor *tps)
{
struct ast_taskprocessor_local local;
diff --git a/main/threadpool.c b/main/threadpool.c
index 9cd33ab1a..6240b7329 100644
--- a/main/threadpool.c
+++ b/main/threadpool.c
@@ -1384,10 +1384,12 @@ struct ast_taskprocessor *ast_threadpool_serializer_group(const char *name,
ao2_ref(ser, -1);
return NULL;
}
- /* ser ref transferred to listener */
tps = ast_taskprocessor_create_with_listener(name, listener);
- if (tps && shutdown_group) {
+ if (!tps) {
+ /* ser ref transferred to listener but not cleaned without tps */
+ ao2_ref(ser, -1);
+ } else if (shutdown_group) {
serializer_shutdown_group_inc(shutdown_group);
}
diff --git a/main/translate.c b/main/translate.c
index 6a39bac1a..73e03a82e 100644
--- a/main/translate.c
+++ b/main/translate.c
@@ -494,7 +494,7 @@ struct ast_trans_pvt *ast_translator_build_path(struct ast_format *dst, struct a
AST_RWLIST_UNLOCK(&translators);
return NULL;
}
- 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)))) {
+ if ((t->dst_codec.sample_rate == ast_format_get_sample_rate(dst)) && (t->dst_codec.type == ast_format_get_type(dst))) {
explicit_dst = dst;
}
if (!(cur = newpvt(t, explicit_dst))) {
diff --git a/main/utils.c b/main/utils.c
index 95eda1102..1a034c101 100644
--- a/main/utils.c
+++ b/main/utils.c
@@ -2425,7 +2425,7 @@ char *ast_utils_which(const char *binary, char *fullpath, size_t fullpath_size)
return NULL;
}
-void ast_do_crash(void)
+void DO_CRASH_NORETURN ast_do_crash(void)
{
#if defined(DO_CRASH)
abort();
@@ -2438,7 +2438,7 @@ void ast_do_crash(void)
}
#if defined(AST_DEVMODE)
-void __ast_assert_failed(int condition, const char *condition_str, const char *file, int line, const char *function)
+void DO_CRASH_NORETURN __ast_assert_failed(int condition, const char *condition_str, const char *file, int line, const char *function)
{
/*
* Attempt to put it into the logger, but hope that at least
@@ -2493,7 +2493,7 @@ void ast_set_default_eid(struct ast_eid *eid)
return;
}
for (x = 0; x < MAXIF; x++) {
- static const char *prefixes[] = { "eth", "em" };
+ static const char *prefixes[] = { "eth", "em", "eno", "ens" };
unsigned int i;
for (i = 0; i < ARRAY_LEN(prefixes); i++) {
@@ -2544,7 +2544,7 @@ void ast_set_default_eid(struct ast_eid *eid)
}
#endif
#endif
- ast_debug(1, "No ethernet interface found for seeding global EID. You will have to set it manually.\n");
+ ast_log(LOG_WARNING, "No ethernet interface found for seeding global EID. You will have to set it manually.\n");
}
int ast_str_to_eid(struct ast_eid *eid, const char *s)
@@ -2569,6 +2569,14 @@ int ast_eid_cmp(const struct ast_eid *eid1, const struct ast_eid *eid2)
return memcmp(eid1, eid2, sizeof(*eid1));
}
+int ast_eid_is_empty(const struct ast_eid *eid)
+{
+ struct ast_eid empty_eid;
+
+ memset(&empty_eid, 0, sizeof(empty_eid));
+ return memcmp(eid, &empty_eid, sizeof(empty_eid)) ? 0 : 1;
+}
+
int ast_file_is_readable(const char *filename)
{
#if defined(HAVE_EACCESS) || defined(HAVE_EUIDACCESS)
diff --git a/menuselect/menuselect.c b/menuselect/menuselect.c
index 6136135aa..da972d8b1 100644
--- a/menuselect/menuselect.c
+++ b/menuselect/menuselect.c
@@ -103,18 +103,6 @@ struct dep_file {
};
AST_LIST_HEAD_NOLOCK_STATIC(deps_file, dep_file);
-#if !defined(ast_strdupa) && defined(__GNUC__)
-#define ast_strdupa(s) \
- (__extension__ \
- ({ \
- const char *__old = (s); \
- size_t __len = strlen(__old) + 1; \
- char *__new = __builtin_alloca(__len); \
- memcpy (__new, __old, __len); \
- __new; \
- }))
-#endif
-
/*! \brief return a pointer to the first non-whitespace character */
static inline char *skip_blanks(char *str)
{
@@ -246,6 +234,10 @@ static enum support_level_values string_to_support_level(const char *support_lev
return SUPPORT_DEPRECATED;
}
+ if (!strcasecmp(support_level, "external")) {
+ return SUPPORT_EXTERNAL;
+ }
+
return SUPPORT_UNSPECIFIED;
}
@@ -259,6 +251,8 @@ static const char *support_level_to_string(enum support_level_values support_lev
return "Extended";
case SUPPORT_DEPRECATED:
return "Deprecated";
+ case SUPPORT_EXTERNAL:
+ return "External";
default:
return "Unspecified";
}
@@ -392,6 +386,11 @@ static int process_xml_use_node(xmlNode *node, struct member *mem)
return process_xml_ref_node(node, mem, &mem->uses);
}
+static int process_xml_member_data_node(xmlNode *node, struct member *mem)
+{
+ return 0;
+}
+
static int process_xml_unknown_node(xmlNode *node, struct member *mem)
{
fprintf(stderr, "Encountered unknown node: %s\n", node->name);
@@ -410,6 +409,7 @@ static const struct {
{ "depend", process_xml_depend_node },
{ "conflict", process_xml_conflict_node },
{ "use", process_xml_use_node },
+ { "member_data", process_xml_member_data_node },
};
static node_handler lookup_node_handler(xmlNode *node)
diff --git a/menuselect/menuselect.h b/menuselect/menuselect.h
index 112f1c88c..bf2bf4c4f 100644
--- a/menuselect/menuselect.h
+++ b/menuselect/menuselect.h
@@ -70,6 +70,8 @@ struct member {
const char *touch_on_change;
const char *support_level;
const char *replacement;
+ /*! member_data is just an opaque, member-specific string */
+ const char *member_data;
/*! This module is currently selected */
unsigned int enabled:1;
/*! This module was enabled when the config was loaded */
@@ -105,7 +107,8 @@ enum support_level_values {
SUPPORT_EXTENDED = 1,
SUPPORT_DEPRECATED = 2,
SUPPORT_UNSPECIFIED = 3,
- SUPPORT_COUNT = 4, /* Keep this item at the end of the list. Tracks total number of support levels. */
+ SUPPORT_EXTERNAL = 4,
+ SUPPORT_COUNT = 5, /* Keep this item at the end of the list. Tracks total number of support levels. */
};
AST_LIST_HEAD_NOLOCK(support_level_bucket, member);
@@ -161,4 +164,16 @@ static inline int strlen_zero(const char *s)
return (!s || (*s == '\0'));
}
+#if !defined(ast_strdupa) && defined(__GNUC__)
+#define ast_strdupa(s) \
+ (__extension__ \
+ ({ \
+ const char *__old = (s); \
+ size_t __len = strlen(__old) + 1; \
+ char *__new = __builtin_alloca(__len); \
+ memcpy (__new, __old, __len); \
+ __new; \
+ }))
+#endif
+
#endif /* MENUSELECT_H */
diff --git a/menuselect/menuselect_curses.c b/menuselect/menuselect_curses.c
index 00645927a..260cf69a7 100644
--- a/menuselect/menuselect_curses.c
+++ b/menuselect/menuselect_curses.c
@@ -194,46 +194,49 @@ static void display_mem_info(WINDOW *menu, struct member *mem, int start_y, int
int start_x = (max_x / 2 - MEMBER_INFO_LEFT_ADJ);
int maxlen = (max_x - start_x);
- wmove(menu, end - start_y + 1, start_x);
+ wmove(menu, end - start_y + 1, 0);
wclrtoeol(menu);
- wmove(menu, end - start_y + 2, start_x);
+ wmove(menu, end - start_y + 2, 0);
wclrtoeol(menu);
- wmove(menu, end - start_y + 3, start_x);
+ wmove(menu, end - start_y + 3, 0);
wclrtoeol(menu);
- wmove(menu, end - start_y + 4, start_x);
+ wmove(menu, end - start_y + 4, 0);
wclrtoeol(menu);
- wmove(menu, end - start_y + 5, start_x);
+ wmove(menu, end - start_y + 5, 0);
wclrtoeol(menu);
- wmove(menu, end - start_y + 6, start_x);
+ wmove(menu, end - start_y + 6, 0);
+ wclrtoeol(menu);
+ wmove(menu, end - start_y + 7, 0);
wclrtoeol(menu);
if (mem->displayname) {
- int name_len = strlen(mem->displayname);
+ char buf[maxlen + 1];
+ char *displayname = ast_strdupa(mem->displayname);
+ char *word;
+ int current_line = 1;
+ int new_line = 1;
+ buf[0] = '\0';
wmove(menu, end - start_y + 1, start_x);
- if (name_len > maxlen) {
- char *last_space;
- char *line_1 = strdup(mem->displayname);
-
- if (line_1) {
- line_1[maxlen] = '\0';
- last_space = strrchr(line_1, ' ');
- if (last_space) {
- *last_space = '\0';
- }
- waddstr(menu, line_1);
- wmove(menu, end - start_y + 2, start_x);
- waddstr(menu, &mem->displayname[last_space - line_1]);
- free(line_1);
- } else {
- waddstr(menu, (char *) mem->displayname);
+
+ while ((word = strsep(&displayname, " "))) {
+ if ((strlen(buf) + strlen(word) + 1) > maxlen) {
+ waddstr(menu, buf);
+ current_line++;
+ wmove(menu, end - start_y + current_line, start_x);
+ buf[0] = '\0';
+ new_line = 1;
}
- } else {
- waddstr(menu, (char *) mem->displayname);
+ sprintf(buf, "%s%*.*s%s", buf, new_line ? 0 : 1, new_line ? 0 : 1, " ", word);
+ new_line = 0;
+ }
+ if (strlen(buf)) {
+ waddstr(menu, buf);
}
}
+
if (!AST_LIST_EMPTY(&mem->deps)) {
- wmove(menu, end - start_y + 3, start_x);
+ wmove(menu, end - start_y + 4, start_x);
strcpy(buf, "Depends on: ");
AST_LIST_TRAVERSE(&mem->deps, dep, list) {
strncat(buf, dep->displayname, sizeof(buf) - strlen(buf) - 1);
@@ -244,7 +247,7 @@ static void display_mem_info(WINDOW *menu, struct member *mem, int start_y, int
waddstr(menu, buf);
}
if (!AST_LIST_EMPTY(&mem->uses)) {
- wmove(menu, end - start_y + 4, start_x);
+ wmove(menu, end - start_y + 5, start_x);
strcpy(buf, "Can use: ");
AST_LIST_TRAVERSE(&mem->uses, use, list) {
strncat(buf, use->displayname, sizeof(buf) - strlen(buf) - 1);
@@ -255,7 +258,7 @@ static void display_mem_info(WINDOW *menu, struct member *mem, int start_y, int
waddstr(menu, buf);
}
if (!AST_LIST_EMPTY(&mem->conflicts)) {
- wmove(menu, end - start_y + 5, start_x);
+ wmove(menu, end - start_y + 6, start_x);
strcpy(buf, "Conflicts with: ");
AST_LIST_TRAVERSE(&mem->conflicts, con, list) {
strncat(buf, con->displayname, sizeof(buf) - strlen(buf) - 1);
@@ -268,7 +271,7 @@ static void display_mem_info(WINDOW *menu, struct member *mem, int start_y, int
if (!mem->is_separator) { /* Separators lack support levels */
{ /* support level */
- wmove(menu, end - start_y + 6, start_x);
+ wmove(menu, end - start_y + 7, start_x);
snprintf(buf, sizeof(buf), "Support Level: %s", mem->support_level);
if (mem->replacement && *mem->replacement) {
char buf2[64];
diff --git a/menuselect/menuselect_gtk.c b/menuselect/menuselect_gtk.c
index cd31b3580..9379d7de6 100644
--- a/menuselect/menuselect_gtk.c
+++ b/menuselect/menuselect_gtk.c
@@ -16,6 +16,8 @@ enum {
COLUMN_USES,
/*! Conflicts */
COLUMN_CNFS,
+ /*! Description */
+ COLUMN_DESC,
/*! Number of columns, must be the last element in the enum */
NUM_COLUMNS,
};
@@ -254,7 +256,8 @@ int run_menu(void)
G_TYPE_BOOLEAN, /* COLUMN_SELECTED */
G_TYPE_STRING, /* COLUMN_DEPS */
G_TYPE_STRING, /* COLUMN_USES */
- G_TYPE_STRING); /* COLUMN_CNFS */
+ G_TYPE_STRING, /* COLUMN_CNFS */
+ G_TYPE_STRING); /* COLUMN_DESC */
AST_LIST_TRAVERSE(&categories, cat, list) {
GtkTreeIter iter, iter2;
@@ -307,6 +310,7 @@ int run_menu(void)
COLUMN_DEPS, dep_buf,
COLUMN_USES, use_buf,
COLUMN_CNFS, cnf_buf,
+ COLUMN_DESC, mem->displayname,
-1);
}
}
@@ -344,6 +348,11 @@ int run_menu(void)
renderer, "text", COLUMN_CNFS, NULL);
gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Description",
+ renderer, "text", COLUMN_DESC, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
+
g_signal_connect(tree, "row-activated", (GCallback) row_activated_handler, store);
gtk_container_add(GTK_CONTAINER(s_window), GTK_WIDGET(tree));
diff --git a/menuselect/menuselect_newt.c b/menuselect/menuselect_newt.c
index fca558781..bc03f9b1b 100644
--- a/menuselect/menuselect_newt.c
+++ b/menuselect/menuselect_newt.c
@@ -326,7 +326,7 @@ int run_menu(void)
newtFormAddComponent(form, subOptions);
newtComponentAddCallback(subOptions, category_menu_callback, NULL);
- memberNameTextbox = newtTextbox(2, y - 13, x - 10, 1, 0);
+ memberNameTextbox = newtTextbox(2, y - 13, x - 10, 2, NEWT_FLAG_WRAP);
dependsLabel = newtLabel(2, y - 11, " Depends on:");
usesLabel = newtLabel(2, y - 10, " Can use:");
conflictsLabel = newtLabel(2, y - 9, "Conflicts with:");
diff --git a/pbx/pbx_dundi.c b/pbx/pbx_dundi.c
index 51801f45f..5ca8a8568 100644
--- a/pbx/pbx_dundi.c
+++ b/pbx/pbx_dundi.c
@@ -4847,6 +4847,9 @@ static int set_config(char *config_file, struct sockaddr_in* sin, int reload)
ast_log(LOG_WARNING, "Unable to get host name!\n");
AST_LIST_LOCK(&peers);
+ if (ast_eid_is_empty(&ast_eid_default)) {
+ ast_log(LOG_WARNING, "Entity ID is not set.\n");
+ }
memcpy(&global_eid, &ast_eid_default, sizeof(global_eid));
global_storehistory = 0;
diff --git a/res/ael/pval.c b/res/ael/pval.c
index d72ef0d59..07545f659 100644
--- a/res/ael/pval.c
+++ b/res/ael/pval.c
@@ -3355,9 +3355,9 @@ static int gen_prios(struct ael_extension *exten, char *label, pval *statement,
#ifdef OLD_RAND_ACTION
struct ael_priority *rand_test, *rand_end, *rand_skip;
#endif
- char *buf1;
- char *buf2;
- char *new_label;
+ RAII_VAR(char *, buf1, NULL, free);
+ RAII_VAR(char *, buf2, NULL, free);
+ RAII_VAR(char *, new_label, NULL, free);
char *strp, *strp2;
int default_exists;
int local_control_statement_count;
@@ -4191,9 +4191,6 @@ static int gen_prios(struct ael_extension *exten, char *label, pval *statement,
break;
}
}
- free(buf1);
- free(buf2);
- free(new_label);
return 0;
}
@@ -5052,7 +5049,10 @@ int pvalCheckType( pval *p, char *funcname, pvaltype type )
pval *pvalCreateNode( pvaltype type )
{
pval *p = calloc(1,sizeof(pval)); /* why, oh why, don't I use ast_calloc? Way, way, way too messy if I do! */
- p->type = type; /* remember, this can be used externally or internally to asterisk */
+ /* remember, this can be used externally or internally to asterisk */
+ if (p) {
+ p->type = type;
+ }
return p;
}
@@ -5413,14 +5413,30 @@ void pvalIncludesAddInclude( pval *p, const char *include )
void pvalIncludesAddIncludeWithTimeConstraints( pval *p, const char *include, char *hour_range, char *dom_range, char *dow_range, char *month_range )
{
- pval *hr = pvalCreateNode(PV_WORD);
- pval *dom = pvalCreateNode(PV_WORD);
- pval *dow = pvalCreateNode(PV_WORD);
- pval *mon = pvalCreateNode(PV_WORD);
- pval *s = pvalCreateNode(PV_WORD);
-
- if (!pvalCheckType(p, "pvalIncludeAddIncludeWithTimeConstraints", PV_INCLUDES))
+ pval *hr;
+ pval *dom;
+ pval *dow;
+ pval *mon;
+ pval *s;
+
+ if (!pvalCheckType(p, "pvalIncludeAddIncludeWithTimeConstraints", PV_INCLUDES)) {
+ return;
+ }
+
+ hr = pvalCreateNode(PV_WORD);
+ dom = pvalCreateNode(PV_WORD);
+ dow = pvalCreateNode(PV_WORD);
+ mon = pvalCreateNode(PV_WORD);
+ s = pvalCreateNode(PV_WORD);
+
+ if (!hr || !dom || !dow || !mon || !s) {
+ destroy_pval(hr);
+ destroy_pval(dom);
+ destroy_pval(dow);
+ destroy_pval(mon);
+ destroy_pval(s);
return;
+ }
s->u1.str = (char *)include;
p->u1.list = linku1(p->u1.list, s);
@@ -5667,12 +5683,28 @@ char* pvalIfGetCondition( pval *p )
void pvalIfTimeSetCondition( pval *p, char *hour_range, char *dow_range, char *dom_range, char *mon_range ) /* time range format: 24-hour format begin-end|dow range|dom range|month range */
{
- pval *hr = pvalCreateNode(PV_WORD);
- pval *dow = pvalCreateNode(PV_WORD);
- pval *dom = pvalCreateNode(PV_WORD);
- pval *mon = pvalCreateNode(PV_WORD);
- if (!pvalCheckType(p, "pvalIfTimeSetCondition", PV_IFTIME))
+ pval *hr;
+ pval *dow;
+ pval *dom;
+ pval *mon;
+
+ if (!pvalCheckType(p, "pvalIfTimeSetCondition", PV_IFTIME)) {
return;
+ }
+
+ hr = pvalCreateNode(PV_WORD);
+ dow = pvalCreateNode(PV_WORD);
+ dom = pvalCreateNode(PV_WORD);
+ mon = pvalCreateNode(PV_WORD);
+
+ if (!hr || !dom || !dow || !mon) {
+ destroy_pval(hr);
+ destroy_pval(dom);
+ destroy_pval(dow);
+ destroy_pval(mon);
+ return;
+ }
+
pvalWordSetString(hr, hour_range);
pvalWordSetString(dow, dow_range);
pvalWordSetString(dom, dom_range);
diff --git a/res/ari/resource_bridges.c b/res/ari/resource_bridges.c
index a86f3129c..39709d022 100644
--- a/res/ari/resource_bridges.c
+++ b/res/ari/resource_bridges.c
@@ -278,6 +278,7 @@ struct bridge_channel_control_thread_data {
struct ast_channel *bridge_channel;
struct stasis_app_control *control;
struct stasis_forward *forward;
+ char bridge_id[0];
};
static void *bridge_channel_control_thread(void *data)
@@ -286,6 +287,7 @@ static void *bridge_channel_control_thread(void *data)
struct ast_channel *bridge_channel = thread_data->bridge_channel;
struct stasis_app_control *control = thread_data->control;
struct stasis_forward *forward = thread_data->forward;
+ char *bridge_id = ast_strdupa(thread_data->bridge_id);
RAII_VAR(struct ast_callid *, callid, ast_channel_callid(bridge_channel), ast_callid_cleanup);
@@ -299,6 +301,7 @@ static void *bridge_channel_control_thread(void *data)
stasis_app_control_execute_until_exhausted(bridge_channel, control);
stasis_app_control_flush_queue(control);
+ stasis_app_bridge_playback_channel_remove(bridge_id, control);
stasis_forward_cancel(forward);
ao2_cleanup(control);
ast_hangup(bridge_channel);
@@ -464,8 +467,9 @@ static void ari_bridges_play_new(const char *args_media,
}
/* Give play_channel and control reference to the thread data */
- thread_data = ast_calloc(1, sizeof(*thread_data));
+ thread_data = ast_malloc(sizeof(*thread_data) + strlen(bridge->uniqueid) + 1);
if (!thread_data) {
+ stasis_app_bridge_playback_channel_remove((char *)bridge->uniqueid, control);
ast_ari_response_alloc_failed(response);
return;
}
@@ -473,8 +477,11 @@ static void ari_bridges_play_new(const char *args_media,
thread_data->bridge_channel = play_channel;
thread_data->control = control;
thread_data->forward = channel_forward;
+ /* Safe */
+ strcpy(thread_data->bridge_id, bridge->uniqueid);
if (ast_pthread_create_detached(&threadid, NULL, bridge_channel_control_thread, thread_data)) {
+ stasis_app_bridge_playback_channel_remove((char *)bridge->uniqueid, control);
ast_ari_response_alloc_failed(response);
ast_free(thread_data);
return;
diff --git a/res/ari/resource_channels.h b/res/ari/resource_channels.h
index 5bb6f7f1e..dd20415ff 100644
--- a/res/ari/resource_channels.h
+++ b/res/ari/resource_channels.h
@@ -78,7 +78,7 @@ struct ast_ari_channels_originate_args {
const char *other_channel_id;
/*! The unique id of the channel which is originating this one. */
const char *originator;
- /*! The format name capability list to use if originator is not specified. Ex. "ulaw,slin16". Format names an be found with "core show codecs". */
+ /*! The format name capability list to use if originator is not specified. Ex. "ulaw,slin16". Format names can be found with "core show codecs". */
const char *formats;
};
/*!
@@ -143,7 +143,7 @@ struct ast_ari_channels_originate_with_id_args {
const char *other_channel_id;
/*! The unique id of the channel which is originating this one. */
const char *originator;
- /*! The format name capability list to use if originator is not specified. Ex. "ulaw,slin16". Format names an be found with "core show codecs". */
+ /*! The format name capability list to use if originator is not specified. Ex. "ulaw,slin16". Format names can be found with "core show codecs". */
const char *formats;
};
/*!
diff --git a/res/res_agi.c b/res/res_agi.c
index e3839dd6d..06df75293 100644
--- a/res/res_agi.c
+++ b/res/res_agi.c
@@ -22,7 +22,6 @@
*
* \author Mark Spencer <markster@digium.com>
*
- * \todo Convert the rest of the AGI commands over to XML documentation
*/
/*** MODULEINFO
@@ -85,6 +84,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</description>
<see-also>
<ref type="agi">hangup</ref>
+ <ref type="application">AGI</ref>
</see-also>
</agi>
<agi name="asyncagi break" language="en_US">
@@ -98,6 +98,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</description>
<see-also>
<ref type="agi">hangup</ref>
+ <ref type="application">AGI</ref>
</see-also>
</agi>
<agi name="channel status" language="en_US">
@@ -138,6 +139,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</enum>
</enumlist>
</description>
+ <see-also>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="control stream file" language="en_US">
<synopsis>
@@ -188,6 +192,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</variable>
</variablelist>
</description>
+ <see-also>
+ <ref type="agi">get option</ref>
+ <ref type="agi">control stream file</ref>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="database del" language="en_US">
<synopsis>
@@ -203,6 +212,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>Returns <literal>1</literal> if successful, <literal>0</literal>
otherwise.</para>
</description>
+ <see-also>
+ <ref type="agi">database get</ref>
+ <ref type="agi">database put</ref>
+ <ref type="agi">database deltree</ref>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="database deltree" language="en_US">
<synopsis>
@@ -217,6 +232,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
within a <replaceable>family</replaceable> in the Asterisk database.</para>
<para>Returns <literal>1</literal> if successful, <literal>0</literal> otherwise.</para>
</description>
+ <see-also>
+ <ref type="agi">database get</ref>
+ <ref type="agi">database put</ref>
+ <ref type="agi">database del</ref>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="database get" language="en_US">
<synopsis>
@@ -234,6 +255,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
in parenthesis.</para>
<para>Example return code: 200 result=1 (testvariable)</para>
</description>
+ <see-also>
+ <ref type="agi">database put</ref>
+ <ref type="agi">database del</ref>
+ <ref type="agi">database deltree</ref>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="database put" language="en_US">
<synopsis>
@@ -250,6 +277,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<replaceable>value</replaceable>.</para>
<para>Returns <literal>1</literal> if successful, <literal>0</literal> otherwise.</para>
</description>
+ <see-also>
+ <ref type="agi">database get</ref>
+ <ref type="agi">database del</ref>
+ <ref type="agi">database deltree</ref>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="exec" language="en_US">
<synopsis>
@@ -265,6 +298,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>Returns whatever the <replaceable>application</replaceable> returns, or
<literal>-2</literal> on failure to find <replaceable>application</replaceable>.</para>
</description>
+ <see-also>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="get data" language="en_US">
<synopsis>
@@ -279,6 +315,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>Stream the given <replaceable>file</replaceable>, and receive DTMF data.</para>
<para>Returns the digits received from the channel at the other end.</para>
</description>
+ <see-also>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="get full variable" language="en_US">
<synopsis>
@@ -295,6 +334,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
variables, unlike GET VARIABLE.</para>
<para>Example return code: 200 result=1 (testvariable)</para>
</description>
+ <see-also>
+ <ref type="agi">get variable</ref>
+ <ref type="agi">set variable</ref>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="get option" language="en_US">
<synopsis>
@@ -310,6 +354,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</description>
<see-also>
<ref type="agi">stream file</ref>
+ <ref type="agi">control stream file</ref>
+ <ref type="application">AGI</ref>
</see-also>
</agi>
<agi name="get variable" language="en_US">
@@ -325,6 +371,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
the variable in parentheses.</para>
<para>Example return code: 200 result=1 (testvariable)</para>
</description>
+ <see-also>
+ <ref type="agi">get full variable</ref>
+ <ref type="agi">set variable</ref>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="hangup" language="en_US">
<synopsis>
@@ -337,6 +388,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>Hangs up the specified channel. If no channel name is given, hangs
up the current channel</para>
</description>
+ <see-also>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="noop" language="en_US">
<synopsis>
@@ -346,6 +400,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<description>
<para>Does nothing.</para>
</description>
+ <see-also>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="receive char" language="en_US">
<synopsis>
@@ -363,6 +420,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
if one is received, or <literal>0</literal> if the channel does not support
text reception. Returns <literal>-1</literal> only on error/hangup.</para>
</description>
+ <see-also>
+ <ref type="agi">receive text</ref>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="receive text" language="en_US">
<synopsis>
@@ -379,6 +440,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
do not support the reception of text. Returns <literal>-1</literal> for failure
or <literal>1</literal> for success, and the string in parenthesis.</para>
</description>
+ <see-also>
+ <ref type="agi">receive char</ref>
+ <ref type="agi">send text</ref>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="record file" language="en_US">
<synopsis>
@@ -404,6 +470,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
lack of dtmf digits or reaching <replaceable>timeout</replaceable>. <replaceable>silence</replaceable>
value must be preceded by <literal>s=</literal> and is also optional.</para>
</description>
+ <see-also>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="say alpha" language="en_US">
<synopsis>
@@ -419,6 +488,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
without a digit being pressed, or the ASCII numerical value of the digit if one
was pressed or <literal>-1</literal> on error/hangup.</para>
</description>
+ <see-also>
+ <ref type="agi">say digits</ref>
+ <ref type="agi">say number</ref>
+ <ref type="agi">say phonetic</ref>
+ <ref type="agi">say date</ref>
+ <ref type="agi">say time</ref>
+ <ref type="agi">say datetime</ref>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="say digits" language="en_US">
<synopsis>
@@ -434,6 +512,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
without a digit being pressed, or the ASCII numerical value of the digit if one
was pressed or <literal>-1</literal> on error/hangup.</para>
</description>
+ <see-also>
+ <ref type="agi">say alpha</ref>
+ <ref type="agi">say number</ref>
+ <ref type="agi">say phonetic</ref>
+ <ref type="agi">say date</ref>
+ <ref type="agi">say time</ref>
+ <ref type="agi">say datetime</ref>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="say number" language="en_US">
<synopsis>
@@ -450,6 +537,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
completes without a digit being pressed, or the ASCII numerical value of
the digit if one was pressed or <literal>-1</literal> on error/hangup.</para>
</description>
+ <see-also>
+ <ref type="agi">say alpha</ref>
+ <ref type="agi">say digits</ref>
+ <ref type="agi">say phonetic</ref>
+ <ref type="agi">say date</ref>
+ <ref type="agi">say time</ref>
+ <ref type="agi">say datetime</ref>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="say phonetic" language="en_US">
<synopsis>
@@ -465,6 +561,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
playback completes without a digit pressed, the ASCII numerical value of the digit
if one was pressed, or <literal>-1</literal> on error/hangup.</para>
</description>
+ <see-also>
+ <ref type="agi">say alpha</ref>
+ <ref type="agi">say digits</ref>
+ <ref type="agi">say number</ref>
+ <ref type="agi">say date</ref>
+ <ref type="agi">say time</ref>
+ <ref type="agi">say datetime</ref>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="say date" language="en_US">
<synopsis>
@@ -483,6 +588,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
completes without a digit being pressed, or the ASCII numerical value of the
digit if one was pressed or <literal>-1</literal> on error/hangup.</para>
</description>
+ <see-also>
+ <ref type="agi">say alpha</ref>
+ <ref type="agi">say digits</ref>
+ <ref type="agi">say number</ref>
+ <ref type="agi">say phonetic</ref>
+ <ref type="agi">say time</ref>
+ <ref type="agi">say datetime</ref>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="say time" language="en_US">
<synopsis>
@@ -501,6 +615,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
without a digit being pressed, or the ASCII numerical value of the digit if
one was pressed or <literal>-1</literal> on error/hangup.</para>
</description>
+ <see-also>
+ <ref type="agi">say alpha</ref>
+ <ref type="agi">say digits</ref>
+ <ref type="agi">say number</ref>
+ <ref type="agi">say phonetic</ref>
+ <ref type="agi">say date</ref>
+ <ref type="agi">say datetime</ref>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="say datetime" language="en_US">
<synopsis>
@@ -528,6 +651,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
completes without a digit being pressed, or the ASCII numerical value of the
digit if one was pressed or <literal>-1</literal> on error/hangup.</para>
</description>
+ <see-also>
+ <ref type="agi">say alpha</ref>
+ <ref type="agi">say digits</ref>
+ <ref type="agi">say number</ref>
+ <ref type="agi">say phonetic</ref>
+ <ref type="agi">say date</ref>
+ <ref type="agi">say time</ref>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="send image" language="en_US">
<synopsis>
@@ -542,6 +674,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
the channel does not support image transmission. Returns <literal>-1</literal>
only on error/hangup. Image names should not include extensions.</para>
</description>
+ <see-also>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="send text" language="en_US">
<synopsis>
@@ -559,6 +694,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
channel does not support text transmission. Returns <literal>-1</literal> only
on error/hangup.</para>
</description>
+ <see-also>
+ <ref type="agi">receive text</ref>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="set autohangup" language="en_US">
<synopsis>
@@ -572,6 +711,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
seconds in the future. Of course it can be hungup before then as well. Setting to
<literal>0</literal> will cause the autohangup feature to be disabled on this channel.</para>
</description>
+ <see-also>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="set callerid" language="en_US">
<synopsis>
@@ -583,6 +725,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<description>
<para>Changes the callerid of the current channel.</para>
</description>
+ <see-also>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="set context" language="en_US">
<synopsis>
@@ -594,6 +739,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<description>
<para>Sets the context for continuation upon exiting the application.</para>
</description>
+ <see-also>
+ <ref type="agi">set extension</ref>
+ <ref type="agi">set priority</ref>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="set extension" language="en_US">
<synopsis>
@@ -605,6 +755,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<description>
<para>Changes the extension for continuation upon exiting the application.</para>
</description>
+ <see-also>
+ <ref type="agi">set context</ref>
+ <ref type="agi">set priority</ref>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="set music" language="en_US">
<synopsis>
@@ -629,6 +784,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
used. This generator will be stopped automatically when playing a file.</para>
<para>Always returns <literal>0</literal>.</para>
</description>
+ <see-also>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="set priority" language="en_US">
<synopsis>
@@ -641,6 +799,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>Changes the priority for continuation upon exiting the application.
The priority must be a valid priority or label.</para>
</description>
+ <see-also>
+ <ref type="agi">set context</ref>
+ <ref type="agi">set extension</ref>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="set variable" language="en_US">
<synopsis>
@@ -653,6 +816,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<description>
<para>Sets a variable to the current channel.</para>
</description>
+ <see-also>
+ <ref type="agi">get variable</ref>
+ <ref type="agi">get full variable</ref>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="stream file" language="en_US">
<synopsis>
@@ -690,6 +858,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</description>
<see-also>
<ref type="agi">control stream file</ref>
+ <ref type="agi">get option</ref>
+ <ref type="application">AGI</ref>
</see-also>
</agi>
<agi name="tdd mode" language="en_US">
@@ -708,6 +878,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>Enable/Disable TDD transmission/reception on a channel. Returns <literal>1</literal> if
successful, or <literal>0</literal> if channel is not TDD-capable.</para>
</description>
+ <see-also>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="verbose" language="en_US">
<synopsis>
@@ -722,6 +895,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
message system. <replaceable>level</replaceable> is the verbose level (1-4).
Always returns <literal>1</literal></para>
</description>
+ <see-also>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="wait for digit" language="en_US">
<synopsis>
@@ -737,6 +913,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
one is received. Use <literal>-1</literal> for the <replaceable>timeout</replaceable> value if
you desire the call to block indefinitely.</para>
</description>
+ <see-also>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="speech create" language="en_US">
<synopsis>
@@ -748,6 +927,16 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<description>
<para>Create a speech object to be used by the other Speech AGI commands.</para>
</description>
+ <see-also>
+ <ref type="agi">speech set</ref>
+ <ref type="agi">speech destroy</ref>
+ <ref type="agi">speech load grammar</ref>
+ <ref type="agi">speech unload grammar</ref>
+ <ref type="agi">speech activate grammar</ref>
+ <ref type="agi">speech deactivate grammar</ref>
+ <ref type="agi">speech recognize</ref>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="speech set" language="en_US">
<synopsis>
@@ -760,6 +949,16 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<description>
<para>Set an engine-specific setting.</para>
</description>
+ <see-also>
+ <ref type="agi">speech create</ref>
+ <ref type="agi">speech destroy</ref>
+ <ref type="agi">speech load grammar</ref>
+ <ref type="agi">speech unload grammar</ref>
+ <ref type="agi">speech activate grammar</ref>
+ <ref type="agi">speech deactivate grammar</ref>
+ <ref type="agi">speech recognize</ref>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="speech destroy" language="en_US">
<synopsis>
@@ -772,6 +971,13 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</description>
<see-also>
<ref type="agi">speech create</ref>
+ <ref type="agi">speech set</ref>
+ <ref type="agi">speech load grammar</ref>
+ <ref type="agi">speech unload grammar</ref>
+ <ref type="agi">speech activate grammar</ref>
+ <ref type="agi">speech deactivate grammar</ref>
+ <ref type="agi">speech recognize</ref>
+ <ref type="application">AGI</ref>
</see-also>
</agi>
<agi name="speech load grammar" language="en_US">
@@ -785,6 +991,16 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<description>
<para>Loads the specified grammar as the specified name.</para>
</description>
+ <see-also>
+ <ref type="agi">speech create</ref>
+ <ref type="agi">speech set</ref>
+ <ref type="agi">speech destroy</ref>
+ <ref type="agi">speech unload grammar</ref>
+ <ref type="agi">speech activate grammar</ref>
+ <ref type="agi">speech deactivate grammar</ref>
+ <ref type="agi">speech recognize</ref>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="speech unload grammar" language="en_US">
<synopsis>
@@ -796,6 +1012,16 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<description>
<para>Unloads the specified grammar.</para>
</description>
+ <see-also>
+ <ref type="agi">speech create</ref>
+ <ref type="agi">speech set</ref>
+ <ref type="agi">speech destroy</ref>
+ <ref type="agi">speech load grammar</ref>
+ <ref type="agi">speech activate grammar</ref>
+ <ref type="agi">speech deactivate grammar</ref>
+ <ref type="agi">speech recognize</ref>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="speech activate grammar" language="en_US">
<synopsis>
@@ -807,6 +1033,16 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<description>
<para>Activates the specified grammar on the speech object.</para>
</description>
+ <see-also>
+ <ref type="agi">speech create</ref>
+ <ref type="agi">speech set</ref>
+ <ref type="agi">speech destroy</ref>
+ <ref type="agi">speech load grammar</ref>
+ <ref type="agi">speech unload grammar</ref>
+ <ref type="agi">speech deactivate grammar</ref>
+ <ref type="agi">speech recognize</ref>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="speech deactivate grammar" language="en_US">
<synopsis>
@@ -818,6 +1054,16 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<description>
<para>Deactivates the specified grammar on the speech object.</para>
</description>
+ <see-also>
+ <ref type="agi">speech create</ref>
+ <ref type="agi">speech set</ref>
+ <ref type="agi">speech destroy</ref>
+ <ref type="agi">speech load grammar</ref>
+ <ref type="agi">speech unload grammar</ref>
+ <ref type="agi">speech activate grammar</ref>
+ <ref type="agi">speech recognize</ref>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<agi name="speech recognize" language="en_US">
<synopsis>
@@ -832,14 +1078,27 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>Plays back given <replaceable>prompt</replaceable> while listening for
speech and dtmf.</para>
</description>
+ <see-also>
+ <ref type="agi">speech create</ref>
+ <ref type="agi">speech set</ref>
+ <ref type="agi">speech destroy</ref>
+ <ref type="agi">speech load grammar</ref>
+ <ref type="agi">speech unload grammar</ref>
+ <ref type="agi">speech activate grammar</ref>
+ <ref type="agi">speech deactivate grammar</ref>
+ <ref type="application">AGI</ref>
+ </see-also>
</agi>
<application name="AGI" language="en_US">
<synopsis>
Executes an AGI compliant application.
</synopsis>
<syntax>
- <parameter name="command" required="true" />
+ <parameter name="command" required="true">
+ <para>How AGI should be invoked on the channel.</para>
+ </parameter>
<parameter name="args">
+ <para>Arguments to pass to the AGI script or server.</para>
<argument name="arg1" required="true" />
<argument name="arg2" multiple="yes" />
</parameter>
@@ -848,21 +1107,72 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>Executes an Asterisk Gateway Interface compliant
program on a channel. AGI allows Asterisk to launch external programs written
in any language to control a telephony channel, play audio, read DTMF digits,
- etc. by communicating with the AGI protocol on <emphasis>stdin</emphasis> and
- <emphasis>stdout</emphasis>. As of <literal>1.6.0</literal>, this channel will
+ etc. by communicating with the AGI protocol.</para>
+ <para>The following variants of AGI exist, and are chosen based on the value
+ passed to <replaceable>command</replaceable>:</para>
+ <enumlist>
+ <enum name="AGI">
+ <para>The classic variant of AGI, this will launch the script
+ specified by <replaceable>command</replaceable> as a new process.
+ Communication with the script occurs on <literal>stdin</literal> and
+ <literal>stdout</literal>. If the full path to the script is not
+ provided, the <directory>astagidir</directory> specified in
+ <filename>asterisk.conf</filename> will be used.
+ </para>
+ </enum>
+ <enum name="FastAGI">
+ <para>Connect Asterisk to a FastAGI server using a TCP connection.
+ The URI to the FastAGI server should be given in the form
+ <literal>[scheme]://host.domain[:port][/script/name]</literal>,
+ where <replaceable>scheme</replaceable> is either <literal>agi</literal>
+ or <literal>hagi</literal>.</para>
+ <para>In the case of <literal>hagi</literal>, an SRV lookup will be
+ performed to try to connect to a list of FastAGI servers. The hostname in
+ the URI must be prefixed with <literal>_agi._tcp</literal>. prior to the DNS resolution. For
+ example, if you specify the URI <literal>hagi://agi.example.com/foo.agi</literal>
+ the DNS query would be for <literal>_agi._tcp.agi.example.com</literal>. You
+ will need to make sure this resolves correctly.</para>
+ </enum>
+ <enum name="AsyncAGI">
+ <para>Use AMI to control the channel in AGI. AGI commands can be invoked
+ using the <literal>AMI</literal> action, with a variety of AGI specific
+ events passed back over the AMI connection. AsyncAGI should be invoked
+ by passing <literal>agi:async</literal> to the <replaceable>command</replaceable>
+ parameter.</para>
+ </enum>
+ </enumlist>
+ <note>
+ <para>As of <literal>1.6.0</literal>, this channel will
not stop dialplan execution on hangup inside of this application. Dialplan
execution will continue normally, even upon hangup until the AGI application
signals a desire to stop (either by exiting or, in the case of a net script, by
- closing the connection). A locally executed AGI script will receive SIGHUP on
- hangup from the channel except when using DeadAGI. A fast AGI server will
- correspondingly receive a HANGUP inline with the command dialog. Both of theses
- signals may be disabled by setting the <variable>AGISIGHUP</variable> channel
- variable to <literal>no</literal> before executing the AGI application.
+ closing the connection).</para>
+ <para>A locally executed AGI script will receive <literal>SIGHUP</literal> on
+ hangup from the channel except when using <literal>DeadAGI</literal>
+ (or when the channel is already hungup). A fast AGI server will
+ correspondingly receive a <literal>HANGUP</literal> inline with the command dialog.
+ Both of these signals may be disabled by setting the <variable>AGISIGHUP</variable>
+ channel variable to <literal>no</literal> before executing the AGI application.
Alternatively, if you would like the AGI application to exit immediately
after a channel hangup is detected, set the <variable>AGIEXITONHANGUP</variable>
variable to <literal>yes</literal>.</para>
- <para>Use the CLI command <literal>agi show commands</literal> to list available agi
- commands.</para>
+ </note>
+ <example title="AGI invocation examples">
+ ; Start the AGI script /tmp/my-cool-script.sh, passing it the contents
+ ; of the channel variable FOO
+ same => n,AGI(/tmp/my-cool-script.sh,${FOO})
+
+ ; Start the AGI script my-cool-script.sh located in the astagidir
+ ; directory, specified in asterisk.conf
+ same => n,AGI(my-cool-script.sh)
+
+ ; Connect to the FastAGI server located at 127.0.0.1 and start the script
+ ; awesome-script
+ same => n,AGI(agi://127.0.0.1/awesome-script)
+
+ ; Start AsyncAGI
+ same => n,AGI(agi:async)
+ </example>
<para>This application sets the following channel variable upon completion:</para>
<variablelist>
<variable name="AGISTATUS">
@@ -876,8 +1186,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</variablelist>
</description>
<see-also>
+ <ref type="manager">AGI</ref>
+ <ref type="managerEvent">AsyncAGIStart</ref>
+ <ref type="managerEvent">AsyncAGIEnd</ref>
<ref type="application">EAGI</ref>
<ref type="application">DeadAGI</ref>
+ <ref type="filename">asterisk.conf</ref>
</see-also>
</application>
<application name="EAGI" language="en_US">
@@ -890,8 +1204,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</syntax>
<description>
<para>Using 'EAGI' provides enhanced AGI, with incoming audio available out of band
- on file descriptor 3.</para>
- <xi:include xpointer="xpointer(/docs/application[@name='AGI']/description/para)" />
+ on file descriptor 3. In all other respects, it behaves in the same fashion as
+ AGI. See the documentation for the <literal>AGI</literal> dialplan application for
+ more information on invoking AGI on a channel.</para>
+ <para>This application sets the following channel variable upon completion:</para>
<xi:include xpointer="xpointer(/docs/application[@name='AGI']/description/variablelist)" />
</description>
<see-also>
@@ -908,7 +1224,16 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<xi:include xpointer="xpointer(/docs/application[@name='AGI']/syntax/parameter[@name='args'])" />
</syntax>
<description>
- <xi:include xpointer="xpointer(/docs/application[@name='AGI']/description/para)" />
+ <warning>
+ <para>This application is deprecated and may be removed in a future version
+ of Asterisk. Use the replacement application <literal>AGI</literal> instead
+ of <literal>DeadAGI</literal>.
+ </para>
+ </warning>
+ <para>Execute AGI on a 'dead' or hungup channel. See the documentation for the
+ <literal>AGI</literal> dialplan application for more information on invoking
+ AGI on a channel.</para>
+ <para>This application sets the following channel variable upon completion:</para>
<xi:include xpointer="xpointer(/docs/application[@name='AGI']/description/variablelist)" />
</description>
<see-also>
@@ -936,6 +1261,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<description>
<para>Add an AGI command to the execute queue of the channel in Async AGI.</para>
</description>
+ <see-also>
+ <ref type="managerEvent">AsyncAGIStart</ref>
+ <ref type="managerEvent">AsyncAGIExec</ref>
+ <ref type="managerEvent">AsyncAGIEnd</ref>
+ </see-also>
</manager>
<managerEvent language="en_US" name="AsyncAGIStart">
<managerEventInstance class="EVENT_FLAG_AGI">
@@ -946,6 +1276,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>URL encoded string read from the AsyncAGI server.</para>
</parameter>
</syntax>
+ <see-also>
+ <ref type="managerEvent">AsyncAGIEnd</ref>
+ <ref type="managerEvent">AsyncAGIExec</ref>
+ <ref type="application">AGI</ref>
+ <ref type="manager">AGI</ref>
+ </see-also>
</managerEventInstance>
</managerEvent>
<managerEvent language="en_US" name="AsyncAGIEnd">
@@ -954,6 +1290,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<syntax>
<channel_snapshot/>
</syntax>
+ <see-also>
+ <ref type="managerEvent">AsyncAGIStart</ref>
+ <ref type="managerEvent">AsyncAGIExec</ref>
+ <ref type="application">AGI</ref>
+ <ref type="manager">AGI</ref>
+ </see-also>
</managerEventInstance>
</managerEvent>
<managerEvent language="en_US" name="AsyncAGIExec">
@@ -968,6 +1310,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>URL encoded result string from the executed AGI command.</para>
</parameter>
</syntax>
+ <see-also>
+ <ref type="managerEvent">AsyncAGIStart</ref>
+ <ref type="managerEvent">AsyncAGIEnd</ref>
+ <ref type="application">AGI</ref>
+ <ref type="manager">AGI</ref>
+ </see-also>
</managerEventInstance>
</managerEvent>
<managerEvent language="en_US" name="AGIExecStart">
@@ -982,6 +1330,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>Random identification number assigned to the execution of this command.</para>
</parameter>
</syntax>
+ <see-also>
+ <ref type="managerEvent">AGIExecEnd</ref>
+ <ref type="application">AGI</ref>
+ </see-also>
</managerEventInstance>
</managerEvent>
<managerEvent language="en_US" name="AGIExecEnd">
@@ -997,6 +1349,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>The text result reason from AGI</para>
</parameter>
</syntax>
+ <see-also>
+ <ref type="managerEvent">AGIExecStart</ref>
+ <ref type="application">AGI</ref>
+ </see-also>
</managerEventInstance>
</managerEvent>
***/
diff --git a/res/res_config_odbc.c b/res/res_config_odbc.c
index 2888d3597..2d991a586 100644
--- a/res/res_config_odbc.c
+++ b/res/res_config_odbc.c
@@ -137,6 +137,7 @@ static SQLHSTMT custom_prepare(struct odbc_obj *obj, void *data)
if (!ast_strlen_zero(cps->extra)) {
const char *newval = cps->extra;
+ ast_debug(1, "Parameter %d = '%s'\n", x, newval);
if (strchr(newval, ';') || strchr(newval, '^')) {
ENCODE_CHUNK(encodebuf, newval);
ast_string_field_set(cps, encoding[x], encodebuf);
diff --git a/res/res_corosync.c b/res/res_corosync.c
index 72da3f129..6bbbc34b9 100644
--- a/res/res_corosync.c
+++ b/res/res_corosync.c
@@ -47,11 +47,16 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
#include "asterisk/app.h"
#include "asterisk/stasis.h"
#include "asterisk/stasis_message_router.h"
+#include "asterisk/stasis_system.h"
AST_RWLOCK_DEFINE_STATIC(event_types_lock);
static void publish_mwi_to_stasis(struct ast_event *event);
static void publish_device_state_to_stasis(struct ast_event *event);
+static void publish_cluster_discovery_to_stasis(struct ast_event *event);
+
+/*! \brief All the nodes that we're aware of */
+static struct ao2_container *nodes;
/*! \brief The internal topic used for message forwarding and pings */
static struct stasis_topic *corosync_aggregate_topic;
@@ -65,6 +70,78 @@ static struct stasis_topic *corosync_topic(void)
return corosync_aggregate_topic;
}
+struct corosync_node {
+ /*! The corosync ID */
+ int id;
+ /*! The Asterisk EID */
+ struct ast_eid eid;
+ /*! The IP address of the node */
+ struct ast_sockaddr addr;
+};
+
+static struct corosync_node *corosync_node_alloc(struct ast_event *event)
+{
+ struct corosync_node *node;
+
+ node = ao2_alloc_options(sizeof(*node), NULL, AO2_ALLOC_OPT_LOCK_NOLOCK);
+ if (!node) {
+ return NULL;
+ }
+
+ memcpy(&node->eid, (struct ast_eid *)ast_event_get_ie_raw(event, AST_EVENT_IE_EID), sizeof(node->eid));
+ node->id = ast_event_get_ie_uint(event, AST_EVENT_IE_NODE_ID);
+ ast_sockaddr_parse(&node->addr, ast_event_get_ie_str(event, AST_EVENT_IE_LOCAL_ADDR), PARSE_PORT_IGNORE);
+
+ return node;
+}
+
+static int corosync_node_hash_fn(const void *obj, const int flags)
+{
+ const struct corosync_node *node;
+ const int *id;
+
+ switch (flags & OBJ_SEARCH_MASK) {
+ case OBJ_SEARCH_KEY:
+ id = obj;
+ break;
+ case OBJ_SEARCH_OBJECT:
+ node = obj;
+ id = &node->id;
+ break;
+ default:
+ ast_assert(0);
+ return 0;
+ }
+ return *id;
+}
+
+static int corosync_node_cmp_fn(void *obj, void *arg, int flags)
+{
+ struct corosync_node *left = obj;
+ struct corosync_node *right = arg;
+ const int *id = arg;
+ int cmp;
+
+ switch (flags & OBJ_SEARCH_MASK) {
+ case OBJ_SEARCH_OBJECT:
+ id = &right->id;
+ /* Fall through */
+ case OBJ_SEARCH_KEY:
+ cmp = (left->id == *id);
+ break;
+ case OBJ_SEARCH_PARTIAL_KEY:
+ cmp = (left->id == right->id);
+ break;
+ default:
+ /* Sort can only work on something with a full or partial key. */
+ ast_assert(0);
+ cmp = 1;
+ break;
+ }
+ return cmp ? CMP_MATCH : 0;
+}
+
+
/*! \brief A payload wrapper around a corosync ping event */
struct corosync_ping_payload {
/*! The corosync ping event being passed over \ref stasis */
@@ -167,6 +244,12 @@ static struct {
.topic_fn = corosync_topic,
.message_type_fn = corosync_ping_message_type,
.publish_to_stasis = publish_corosync_ping_to_stasis, },
+ [AST_EVENT_CLUSTER_DISCOVERY] = { .name = "cluster_discovery",
+ .publish_default = 1,
+ .subscribe_default = 1,
+ .topic_fn = ast_system_topic,
+ .message_type_fn = ast_cluster_discovery_type,
+ .publish_to_stasis = publish_cluster_discovery_to_stasis, },
};
static struct {
@@ -197,6 +280,97 @@ static corosync_cfg_callbacks_t cfg_callbacks = {
.corosync_cfg_shutdown_callback = cfg_shutdown_cb,
};
+/*! \brief Publish cluster discovery to \ref stasis */
+static void publish_cluster_discovery_to_stasis_full(struct corosync_node *node, int joined)
+{
+ struct ast_json *json;
+ struct ast_json_payload *payload;
+ struct stasis_message *message;
+ char eid[18];
+ const char *addr;
+
+ ast_eid_to_str(eid, sizeof(eid), &node->eid);
+ addr = ast_sockaddr_stringify_addr(&node->addr);
+
+ ast_log(AST_LOG_NOTICE, "Node %u (%s) at %s %s the cluster\n",
+ node->id,
+ eid,
+ addr,
+ joined ? "joined" : "left");
+
+ json = ast_json_pack("{s: s, s: i, s: s, s: i}",
+ "address", addr,
+ "node_id", node->id,
+ "eid", eid,
+ "joined", joined);
+ if (!json) {
+ return;
+ }
+
+ payload = ast_json_payload_create(json);
+ if (!payload) {
+ ast_json_unref(json);
+ return;
+ }
+
+ message = stasis_message_create(ast_cluster_discovery_type(), payload);
+ if (!message) {
+ ast_json_unref(json);
+ ao2_ref(payload, -1);
+ return;
+ }
+
+ stasis_publish(ast_system_topic(), message);
+ ast_json_unref(json);
+ ao2_ref(payload, -1);
+ ao2_ref(message, -1);
+}
+
+static void send_cluster_notify(void);
+
+/*! \brief Publish a received cluster discovery \ref ast_event to \ref stasis */
+static void publish_cluster_discovery_to_stasis(struct ast_event *event)
+{
+ struct corosync_node *node;
+ int id = ast_event_get_ie_uint(event, AST_EVENT_IE_NODE_ID);
+ struct ast_eid *event_eid;
+
+ ast_assert(ast_event_get_type(event) == AST_EVENT_CLUSTER_DISCOVERY);
+
+ event_eid = (struct ast_eid *)ast_event_get_ie_raw(event, AST_EVENT_IE_EID);
+ if (!ast_eid_cmp(&ast_eid_default, event_eid)) {
+ /* Don't feed events back in that originated locally. */
+ return;
+ }
+
+ ao2_lock(nodes);
+ node = ao2_find(nodes, &id, OBJ_SEARCH_KEY | OBJ_NOLOCK);
+ if (node) {
+ /* We already know about this node */
+ ao2_unlock(nodes);
+ ao2_ref(node, -1);
+ return;
+ }
+
+ node = corosync_node_alloc(event);
+ if (!node) {
+ ao2_unlock(nodes);
+ return;
+ }
+ ao2_link_flags(nodes, node, OBJ_NOLOCK);
+ ao2_unlock(nodes);
+
+ publish_cluster_discovery_to_stasis_full(node, 1);
+
+ ao2_ref(node, -1);
+
+ /*
+ * When we get news that someone else has joined, we need to let them
+ * know we exist as well.
+ */
+ send_cluster_notify();
+}
+
/*! \brief Publish a received MWI \ref ast_event to \ref stasis */
static void publish_mwi_to_stasis(struct ast_event *event)
{
@@ -228,7 +402,7 @@ static void publish_mwi_to_stasis(struct ast_event *event)
if (ast_publish_mwi_state_full(mailbox, context, (int)new_msgs,
(int)old_msgs, NULL, event_eid)) {
- char eid[16];
+ char eid[18];
ast_eid_to_str(eid, sizeof(eid), event_eid);
ast_log(LOG_WARNING, "Failed to publish MWI message for %s@%s from %s\n",
mailbox, context, eid);
@@ -255,7 +429,7 @@ static void publish_device_state_to_stasis(struct ast_event *event)
}
if (ast_publish_device_state_full(device, state, cachable, event_eid)) {
- char eid[16];
+ char eid[18];
ast_eid_to_str(eid, sizeof(eid), event_eid);
ast_log(LOG_WARNING, "Failed to publish device state message for %s from %s\n",
device, eid);
@@ -342,10 +516,27 @@ static void cpg_deliver_cb(cpg_handle_t handle, const struct cpg_name *group_nam
publish_handler(event);
}
-static void publish_to_corosync(struct stasis_message *message)
+static void publish_event_to_corosync(struct ast_event *event)
{
cs_error_t cs_err;
struct iovec iov;
+
+ iov.iov_base = (void *)event;
+ iov.iov_len = ast_event_get_size(event);
+
+ ast_debug(5, "Publishing event %s (%u) to corosync\n",
+ ast_event_get_type_name(event), ast_event_get_type(event));
+
+ /* The stasis subscription will only exist if we are configured to publish
+ * these events, so just send away. */
+ if ((cs_err = cpg_mcast_joined(cpg_handle, CPG_TYPE_FIFO, &iov, 1)) != CS_OK) {
+ ast_log(LOG_WARNING, "CPG mcast failed (%u) for event %s (%u)\n",
+ cs_err, ast_event_get_type_name(event), ast_event_get_type(event));
+ }
+}
+
+static void publish_to_corosync(struct stasis_message *message)
+{
struct ast_event *event;
event = stasis_message_to_event(message);
@@ -368,17 +559,7 @@ static void publish_to_corosync(struct stasis_message *message)
ast_log(LOG_NOTICE, "Sending event PING from this server with EID: '%s'\n", buf);
}
- iov.iov_base = (void *)event;
- iov.iov_len = ast_event_get_size(event);
-
- ast_debug(5, "Publishing event %s (%u) to corosync\n",
- ast_event_get_type_name(event), ast_event_get_type(event));
-
- /* The stasis subscription will only exist if we are configured to publish
- * these events, so just send away. */
- if ((cs_err = cpg_mcast_joined(cpg_handle, CPG_TYPE_FIFO, &iov, 1)) != CS_OK) {
- ast_log(LOG_WARNING, "CPG mcast failed (%u)\n", cs_err);
- }
+ publish_event_to_corosync(event);
}
static void stasis_message_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message)
@@ -410,9 +591,22 @@ static void cpg_confchg_cb(cpg_handle_t handle, const struct cpg_name *group_nam
{
unsigned int i;
+
+ for (i = 0; i < left_list_entries; i++) {
+ const struct cpg_address *cpg_node = &left_list[i];
+ struct corosync_node* node;
+
+ node = ao2_find(nodes, &cpg_node->nodeid, OBJ_UNLINK | OBJ_SEARCH_KEY);
+ if (!node) {
+ continue;
+ }
+
+ publish_cluster_discovery_to_stasis_full(node, 0);
+ ao2_ref(node, -1);
+ }
+
/* If any new nodes have joined, dump our cache of events we are publishing
* that originated from this server. */
-
if (!joined_list_entries) {
return;
}
@@ -442,6 +636,45 @@ static void cpg_confchg_cb(cpg_handle_t handle, const struct cpg_name *group_nam
}
}
+/*! \brief Informs the cluster of our EID and our IP addresses */
+static void send_cluster_notify(void)
+{
+ struct ast_event *event;
+ unsigned int node_id;
+ cs_error_t cs_err;
+ corosync_cfg_node_address_t corosync_addr;
+ int num_addrs = 0;
+ struct sockaddr *sa;
+ size_t sa_len;
+ char buf[128];
+ int res;
+
+ if ((cs_err = corosync_cfg_local_get(cfg_handle, &node_id)) != CS_OK) {
+ ast_log(LOG_WARNING, "Failed to extract Corosync node ID for this node. Not informing cluster of existance.\n");
+ return;
+ }
+
+ if (((cs_err = corosync_cfg_get_node_addrs(cfg_handle, node_id, 1, &num_addrs, &corosync_addr)) != CS_OK) || (num_addrs < 1)) {
+ ast_log(LOG_WARNING, "Failed to get local Corosync address. Not informing cluster of existance.\n");
+ return;
+ }
+
+ sa = (struct sockaddr *)corosync_addr.address;
+ sa_len = (size_t)corosync_addr.address_length;
+ if ((res = getnameinfo(sa, sa_len, buf, sizeof(buf), NULL, 0, NI_NUMERICHOST))) {
+ ast_log(LOG_WARNING, "Failed to determine name of local Corosync address: %s (%d). Not informing cluster of existance.\n",
+ gai_strerror(res), res);
+ return;
+ }
+
+ event = ast_event_new(AST_EVENT_CLUSTER_DISCOVERY,
+ AST_EVENT_IE_NODE_ID, AST_EVENT_IE_PLTYPE_UINT, node_id,
+ AST_EVENT_IE_LOCAL_ADDR, AST_EVENT_IE_PLTYPE_STR, buf,
+ AST_EVENT_IE_END);
+ publish_event_to_corosync(event);
+ ast_free(event);
+}
+
static void *dispatch_thread_handler(void *data)
{
cs_error_t cs_err;
@@ -463,6 +696,7 @@ static void *dispatch_thread_handler(void *data)
pfd[2].fd = dispatch_thread.alert_pipe[0];
+ send_cluster_notify();
while (!dispatch_thread.stop) {
int res;
@@ -530,6 +764,7 @@ static void *dispatch_thread_handler(void *data)
}
ast_log(LOG_NOTICE, "Corosync recovery complete.\n");
+ send_cluster_notify();
}
}
@@ -858,6 +1093,9 @@ static void cleanup_module(void)
ast_log(LOG_ERROR, "Failed to finalize cfg (%d)\n", (int) cs_err);
}
cfg_handle = 0;
+
+ ao2_cleanup(nodes);
+ nodes = NULL;
}
static int load_module(void)
@@ -865,6 +1103,16 @@ static int load_module(void)
cs_error_t cs_err;
struct cpg_name name;
+ if (ast_eid_is_empty(&ast_eid_default)) {
+ ast_log(LOG_ERROR, "Entity ID is not set.\n");
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ nodes = ao2_container_alloc(23, corosync_node_hash_fn, corosync_node_cmp_fn);
+ if (!nodes) {
+ goto failed;
+ }
+
corosync_aggregate_topic = stasis_topic_create("corosync_aggregate_topic");
if (!corosync_aggregate_topic) {
ast_log(AST_LOG_ERROR, "Failed to create stasis topic for corosync\n");
@@ -919,6 +1167,7 @@ static int load_module(void)
ast_cli_register_multiple(corosync_cli, ARRAY_LEN(corosync_cli));
+
return AST_MODULE_LOAD_SUCCESS;
failed:
diff --git a/res/res_fax.c b/res/res_fax.c
index 6282b13d7..86260d10e 100644
--- a/res/res_fax.c
+++ b/res/res_fax.c
@@ -468,8 +468,6 @@ struct fax_gateway {
struct fax_detect {
/*! \brief the start of our timeout counter */
struct timeval timeout_start;
- /*! \brief faxdetect timeout */
- int timeout;
/*! \brief DSP Processor */
struct ast_dsp *dsp;
/*! \brief original audio formats */
@@ -2846,11 +2844,8 @@ static struct ast_fax_session *fax_v21_session_new (struct ast_channel *chan) {
}
v21_details->caps = AST_FAX_TECH_V21_DETECT;
- if (!(v21_session = fax_session_new(v21_details, chan, NULL, NULL))) {
- ao2_ref(v21_details, -1);
- return NULL;
- }
-
+ v21_session = fax_session_new(v21_details, chan, NULL, NULL);
+ ao2_ref(v21_details, -1);
return v21_session;
}
@@ -3542,13 +3537,13 @@ static void destroy_faxdetect(void *data)
ast_dsp_free(faxdetect->dsp);
faxdetect->dsp = NULL;
}
- ao2_ref(faxdetect->details, -1);
+ ao2_cleanup(faxdetect->details);
ao2_cleanup(faxdetect->orig_format);
}
/*! \brief Create a new fax detect object.
* \param chan the channel attaching to
- * \param timeout remove framehook in this time if set
+ * \param timeout in ms to remove framehook in this time if not zero
* \param flags required options
* \return NULL or a fax gateway object
*/
@@ -3655,8 +3650,9 @@ static struct ast_frame *fax_detect_framehook(struct ast_channel *chan, struct a
return f;
}
- if ((!ast_tvzero(faxdetect->timeout_start) &&
- (ast_tvdiff_ms(ast_tvnow(), faxdetect->timeout_start) > faxdetect->timeout))) {
+ if (!ast_tvzero(faxdetect->timeout_start)
+ && ast_tvdiff_ms(ast_tvnow(), faxdetect->timeout_start) > details->faxdetect_timeout) {
+ ast_debug(1, "FAXOPT(faxdetect) timeout on %s\n", ast_channel_name(chan));
ast_framehook_detach(chan, details->faxdetect_id);
details->faxdetect_id = -1;
return f;
@@ -3704,30 +3700,36 @@ static struct ast_frame *fax_detect_framehook(struct ast_channel *chan, struct a
}
if (result) {
- const char *target_context = S_OR(ast_channel_macrocontext(chan), ast_channel_context(chan));
+ const char *target_context;
+
switch (result) {
case 'f':
case 't':
+ target_context = S_OR(ast_channel_macrocontext(chan), ast_channel_context(chan));
+
ast_channel_unlock(chan);
+ ast_frfree(f);
+ f = &ast_null_frame;
if (ast_exists_extension(chan, target_context, "fax", 1,
S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
- ast_channel_lock(chan);
ast_verb(2, "Redirecting '%s' to fax extension due to %s detection\n",
ast_channel_name(chan), (result == 'f') ? "CNG" : "T38");
pbx_builtin_setvar_helper(chan, "FAXEXTEN", ast_channel_exten(chan));
if (ast_async_goto(chan, target_context, "fax", 1)) {
ast_log(LOG_NOTICE, "Failed to async goto '%s' into fax of '%s'\n", ast_channel_name(chan), target_context);
}
- ast_frfree(f);
- f = &ast_null_frame;
} else {
- ast_channel_lock(chan);
ast_log(LOG_NOTICE, "FAX %s detected but no fax extension in context (%s)\n",
(result == 'f') ? "CNG" : "T38", target_context);
}
+ ast_channel_lock(chan);
+
+ ast_framehook_detach(chan, details->faxdetect_id);
+ details->faxdetect_id = -1;
+ break;
+ default:
+ break;
}
- ast_framehook_detach(chan, details->faxdetect_id);
- details->faxdetect_id = -1;
}
return f;
@@ -3735,7 +3737,7 @@ static struct ast_frame *fax_detect_framehook(struct ast_channel *chan, struct a
/*! \brief Attach a faxdetect framehook object to a channel.
* \param chan the channel to attach to
- * \param timeout remove framehook in this time if set
+ * \param timeout in ms to remove framehook in this time if not zero
* \return the faxdetect structure or NULL on error
* \param flags required options
* \retval -1 error
@@ -4483,8 +4485,14 @@ static int acf_faxopt_write(struct ast_channel *chan, const char *cmd, char *dat
details->gateway_timeout = 0;
if (timeout) {
unsigned int gwtimeout;
- if (sscanf(timeout, "%u", &gwtimeout) == 1) {
- details->gateway_timeout = gwtimeout * 1000;
+
+ if (sscanf(timeout, "%30u", &gwtimeout) == 1) {
+ if (gwtimeout >= 0) {
+ details->gateway_timeout = gwtimeout * 1000;
+ } else {
+ ast_log(LOG_WARNING, "%s(%s) timeout cannot be negative. Ignoring timeout\n",
+ cmd, data);
+ }
} else {
ast_log(LOG_WARNING, "Unsupported timeout '%s' passed to FAXOPT(%s).\n", timeout, data);
}
@@ -4519,11 +4527,18 @@ static int acf_faxopt_write(struct ast_channel *chan, const char *cmd, char *dat
if (ast_true(val) || !strcasecmp(val, "t38") || !strcasecmp(val, "cng")) {
if (details->faxdetect_id < 0) {
- if (timeout && (sscanf(timeout, "%u", &fdtimeout) == 1)) {
- if (fdtimeout > 0) {
- fdtimeout = fdtimeout * 1000;
+ if (timeout) {
+ if (sscanf(timeout, "%30u", &fdtimeout) == 1) {
+ if (fdtimeout >= 0) {
+ fdtimeout *= 1000;
+ } else {
+ ast_log(LOG_WARNING, "%s(%s) timeout cannot be negative. Ignoring timeout\n",
+ cmd, data);
+ fdtimeout = 0;
+ }
} else {
- ast_log(LOG_WARNING, "Timeout cannot be negative ignoring timeout\n");
+ ast_log(LOG_WARNING, "Unsupported timeout '%s' passed to FAXOPT(%s).\n",
+ timeout, data);
}
}
diff --git a/res/res_format_attr_silk.c b/res/res_format_attr_silk.c
index dcbbe4c1c..d52ec7410 100644
--- a/res/res_format_attr_silk.c
+++ b/res/res_format_attr_silk.c
@@ -40,7 +40,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
* \note The only attribute that affects compatibility here is the sample rate.
*/
struct silk_attr {
- unsigned int samplerate;
unsigned int maxbitrate;
unsigned int dtx;
unsigned int fec;
@@ -54,10 +53,15 @@ static void silk_destroy(struct ast_format *format)
ast_free(attr);
}
+static void attr_init(struct silk_attr *attr)
+{
+ memset(attr, 0, sizeof(*attr));
+}
+
static int silk_clone(const struct ast_format *src, struct ast_format *dst)
{
struct silk_attr *original = ast_format_get_attribute_data(src);
- struct silk_attr *attr = ast_calloc(1, sizeof(*attr));
+ struct silk_attr *attr = ast_malloc(sizeof(*attr));
if (!attr) {
return -1;
@@ -65,6 +69,8 @@ static int silk_clone(const struct ast_format *src, struct ast_format *dst)
if (original) {
*attr = *original;
+ } else {
+ attr_init(attr);
}
ast_format_set_attribute_data(dst, attr);
@@ -109,17 +115,17 @@ static void silk_generate_sdp_fmtp(const struct ast_format *format, unsigned int
ast_str_append(str, 0, "a=fmtp:%u maxaveragebitrate=%u\r\n", payload, attr->maxbitrate);
}
- ast_str_append(str, 0, "a=fmtp:%u usedtx=%u\r\n", payload, attr->dtx);
- ast_str_append(str, 0, "a=fmtp:%u useinbandfec=%u\r\n", payload, attr->fec);
+ if (attr->dtx) {
+ ast_str_append(str, 0, "a=fmtp:%u usedtx=%u\r\n", payload, attr->dtx);
+ }
+ if (attr->fec) {
+ ast_str_append(str, 0, "a=fmtp:%u useinbandfec=%u\r\n", payload, attr->fec);
+ }
}
static enum ast_format_cmp_res silk_cmp(const struct ast_format *format1, const struct ast_format *format2)
{
- struct silk_attr *attr1 = ast_format_get_attribute_data(format1);
- struct silk_attr *attr2 = ast_format_get_attribute_data(format2);
-
- if (((!attr1 || !attr1->samplerate) && (!attr2 || !attr2->samplerate)) ||
- (attr1->samplerate == attr2->samplerate)) {
+ if (ast_format_get_sample_rate(format1) == ast_format_get_sample_rate(format2)) {
return AST_FORMAT_CMP_EQUAL;
}
@@ -130,13 +136,10 @@ static struct ast_format *silk_getjoint(const struct ast_format *format1, const
{
struct silk_attr *attr1 = ast_format_get_attribute_data(format1);
struct silk_attr *attr2 = ast_format_get_attribute_data(format2);
- unsigned int samplerate;
struct ast_format *jointformat;
struct silk_attr *attr_res;
- samplerate = attr1->samplerate & attr2->samplerate;
- /* sample rate is the only attribute that has any bearing on if joint capabilities exist or not */
- if (samplerate) {
+ if (ast_format_get_sample_rate(format1) != ast_format_get_sample_rate(format2)) {
return NULL;
}
@@ -145,22 +148,25 @@ static struct ast_format *silk_getjoint(const struct ast_format *format1, const
return NULL;
}
attr_res = ast_format_get_attribute_data(jointformat);
- attr_res->samplerate = samplerate;
- /* Take the lowest max bitrate */
- attr_res->maxbitrate = MIN(attr1->maxbitrate, attr2->maxbitrate);
+ if (!attr1 || !attr2) {
+ attr_init(attr_res);
+ } else {
+ /* Take the lowest max bitrate */
+ attr_res->maxbitrate = MIN(attr1->maxbitrate, attr2->maxbitrate);
- /* 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;
+ /* 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;
- /* 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;
+ /* 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;
- /* Use the maximum packetloss percentage between the two attributes. This affects how
- * much redundancy is used in the FEC. */
- attr_res->packetloss_percentage = MAX(attr1->packetloss_percentage, attr2->packetloss_percentage);
+ /* Use the maximum packetloss percentage between the two attributes. This affects how
+ * much redundancy is used in the FEC. */
+ attr_res->packetloss_percentage = MAX(attr1->packetloss_percentage, attr2->packetloss_percentage);
+ }
return jointformat;
}
@@ -183,9 +189,7 @@ static struct ast_format *silk_set(const struct ast_format *format, const char *
}
attr = ast_format_get_attribute_data(cloned);
- if (!strcasecmp(name, "sample_rate")) {
- attr->samplerate = val;
- } else if (!strcasecmp(name, "max_bitrate")) {
+ if (!strcasecmp(name, "max_bitrate")) {
attr->maxbitrate = val;
} else if (!strcasecmp(name, "dtx")) {
attr->dtx = val;
@@ -205,9 +209,7 @@ 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")) {
+ if (!strcasecmp(name, "max_bitrate")) {
val = &attr->maxbitrate;
} else if (!strcasecmp(name, "dtx")) {
val = &attr->dtx;
diff --git a/res/res_format_attr_siren14.c b/res/res_format_attr_siren14.c
new file mode 100644
index 000000000..dea13aec6
--- /dev/null
+++ b/res/res_format_attr_siren14.c
@@ -0,0 +1,94 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2016, Digium, Inc.
+ *
+ * Joshua Colp <jcolp@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ * \brief Siren14 format attribute interface
+ *
+ * \author Joshua Colp <jcolp@digium.com>
+ */
+
+/*** MODULEINFO
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/format.h"
+
+/* Destroy is a required callback and must exist */
+static void siren14_destroy(struct ast_format *format)
+{
+}
+
+/* Clone is a required callback and must exist */
+static int siren14_clone(const struct ast_format *src, struct ast_format *dst)
+{
+ return 0;
+}
+
+static struct ast_format *siren14_parse_sdp_fmtp(const struct ast_format *format, const char *attributes)
+{
+ unsigned int val;
+
+ if (sscanf(attributes, "bitrate=%30u", &val) == 1) {
+ if (val != 48000) {
+ ast_log(LOG_WARNING, "Got siren14 offer at %u bps, but only 48000 bps supported; ignoring.\n", val);
+ return NULL;
+ }
+ }
+
+ /* We aren't modifying the format and once passed back it won't be touched, so use what we were given */
+ return ao2_bump((struct ast_format *)format);
+}
+
+static void siren14_generate_sdp_fmtp(const struct ast_format *format, unsigned int payload, struct ast_str **str)
+{
+ ast_str_append(str, 0, "a=fmtp:%u bitrate=48000\r\n", payload);
+}
+
+static struct ast_format_interface siren14_interface = {
+ .format_destroy = siren14_destroy,
+ .format_clone = siren14_clone,
+ .format_parse_sdp_fmtp = siren14_parse_sdp_fmtp,
+ .format_generate_sdp_fmtp = siren14_generate_sdp_fmtp,
+};
+
+static int load_module(void)
+{
+ if (ast_format_interface_register("siren14", &siren14_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, "Siren14 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_format_attr_siren7.c b/res/res_format_attr_siren7.c
new file mode 100644
index 000000000..840de4886
--- /dev/null
+++ b/res/res_format_attr_siren7.c
@@ -0,0 +1,94 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2016, Digium, Inc.
+ *
+ * Joshua Colp <jcolp@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ * \brief Siren7 format attribute interface
+ *
+ * \author Joshua Colp <jcolp@digium.com>
+ */
+
+/*** MODULEINFO
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/format.h"
+
+/* Destroy is a required callback and must exist */
+static void siren7_destroy(struct ast_format *format)
+{
+}
+
+/* Clone is a required callback and must exist */
+static int siren7_clone(const struct ast_format *src, struct ast_format *dst)
+{
+ return 0;
+}
+
+static struct ast_format *siren7_parse_sdp_fmtp(const struct ast_format *format, const char *attributes)
+{
+ unsigned int val;
+
+ if (sscanf(attributes, "bitrate=%30u", &val) == 1) {
+ if (val != 32000) {
+ ast_log(LOG_WARNING, "Got Siren7 offer at %u bps, but only 32000 bps supported; ignoring.\n", val);
+ return NULL;
+ }
+ }
+
+ /* We aren't modifying the format and once passed back it won't be touched, so use what we were given */
+ return ao2_bump((struct ast_format *)format);
+}
+
+static void siren7_generate_sdp_fmtp(const struct ast_format *format, unsigned int payload, struct ast_str **str)
+{
+ ast_str_append(str, 0, "a=fmtp:%u bitrate=32000\r\n", payload);
+}
+
+static struct ast_format_interface siren7_interface = {
+ .format_destroy = siren7_destroy,
+ .format_clone = siren7_clone,
+ .format_parse_sdp_fmtp = siren7_parse_sdp_fmtp,
+ .format_generate_sdp_fmtp = siren7_generate_sdp_fmtp,
+};
+
+static int load_module(void)
+{
+ if (ast_format_interface_register("siren7", &siren7_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, "Siren7 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_odbc.c b/res/res_odbc.c
index b2204ff09..31ea29b03 100644
--- a/res/res_odbc.c
+++ b/res/res_odbc.c
@@ -744,6 +744,22 @@ static int aoro2_class_cb(void *obj, void *arg, int flags)
return 0;
}
+unsigned int ast_odbc_get_max_connections(const char *name)
+{
+ struct odbc_class *class;
+ unsigned int max_connections;
+
+ class = ao2_callback(class_container, 0, aoro2_class_cb, (char *) name);
+ if (!class) {
+ return 0;
+ }
+
+ max_connections = class->maxconnections;
+ ao2_ref(class, -1);
+
+ return max_connections;
+}
+
/*
* \brief Determine if the connection has died.
*
diff --git a/res/res_phoneprov.c b/res/res_phoneprov.c
index df93c5bbc..71f875753 100644
--- a/res/res_phoneprov.c
+++ b/res/res_phoneprov.c
@@ -410,10 +410,13 @@ static int load_file(const char *filename, char **ret)
fseek(f, 0, SEEK_END);
len = ftell(f);
fseek(f, 0, SEEK_SET);
- if (!(*ret = ast_malloc(len + 1)))
+ if (!(*ret = ast_malloc(len + 1))) {
+ fclose(f);
return -2;
+ }
if (len != fread(*ret, sizeof(char), len, f)) {
+ fclose(f);
free(*ret);
*ret = NULL;
return -3;
diff --git a/res/res_pjproject.c b/res/res_pjproject.c
index f54c3713e..08699f3ee 100644
--- a/res/res_pjproject.c
+++ b/res/res_pjproject.c
@@ -177,7 +177,6 @@ static void log_forwarder(int level, const char *data, int len)
const char * log_source = "pjproject";
int log_line = 0;
const char *log_func = "<?>";
- int mod_level;
if (pjproject_log_intercept.fd != -1
&& pjproject_log_intercept.thread == pthread_self()) {
@@ -196,10 +195,8 @@ static void log_forwarder(int level, const char *data, int len)
}
if (ast_level == __LOG_DEBUG) {
- /* For levels 3 and up, obey the debug level for res_pjproject */
- mod_level = ast_opt_dbg_module ?
- ast_debug_get_by_module("res_pjproject") : 0;
- if (option_debug < level && mod_level < level) {
+ /* Obey the debug level for res_pjproject */
+ if (!DEBUG_ATLEAST(level)) {
return;
}
}
diff --git a/res/res_pjsip.c b/res/res_pjsip.c
index bbaf77853..aafb3a211 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -24,6 +24,7 @@
#include <pjsip/sip_transaction.h>
#include <pj/timer.h>
#include <pjlib.h>
+#include <pjmedia/errno.h>
#include "asterisk/res_pjsip.h"
#include "res_pjsip/include/res_pjsip_private.h"
@@ -216,10 +217,9 @@
<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>
-
+ <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>
@@ -509,15 +509,15 @@
<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.
+ 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>
+ progress.</synopsis>
<description><para>
If set to <literal>yes</literal>, chan_pjsip will send a 183 Session Progress
when told to indicate ringing and will immediately start sending ringing
@@ -599,6 +599,14 @@
detected.
</para></description>
</configOption>
+ <configOption name="fax_detect_timeout">
+ <synopsis>How long into a call before fax_detect is disabled for the call</synopsis>
+ <description><para>
+ The option determines how many seconds into a call before the
+ fax_detect option is disabled for the call. Setting the value
+ to zero disables the timeout.
+ </para></description>
+ </configOption>
<configOption name="t38_udptl_nat" default="no">
<synopsis>Whether NAT support is enabled on UDPTL sessions</synopsis>
<description><para>
@@ -802,7 +810,7 @@
<configOption name="set_var">
<synopsis>Variable set on a channel involving the endpoint.</synopsis>
<description><para>
- When a new channel is created using the endpoint set the specified
+ When a new channel is created using the endpoint set the specified
variable(s) on that channel. For multiple channel variables specify
multiple 'set_var'(s).
</para></description>
@@ -896,6 +904,15 @@
mask with a slash ('/')
</para></description>
</configOption>
+ <configOption name="subscribe_context">
+ <synopsis>Context for incoming MESSAGE requests.</synopsis>
+ <description><para>
+ If specified, incoming SUBSCRIBE requests will be searched for the matching
+ extension in the indicated context.
+ If no <replaceable>subscribe_context</replaceable> is specified,
+ then the <replaceable>context</replaceable> setting is used.
+ </para></description>
+ </configOption>
</configObject>
<configObject name="auth">
<synopsis>Authentication type</synopsis>
@@ -1166,6 +1183,12 @@
REGISTER requests and is not intended to be configured manually.
</para></description>
</configOption>
+ <configOption name="endpoint">
+ <synopsis>Endpoint name</synopsis>
+ <description><para>
+ The name of the endpoint this contact belongs to
+ </para></description>
+ </configOption>
<configOption name="reg_server">
<synopsis>Asterisk Server name</synopsis>
<description><para>
@@ -1428,9 +1451,9 @@
<synopsis>Value used in User-Agent header for SIP requests and Server header for SIP responses.</synopsis>
</configOption>
<configOption name="regcontext" default="">
- <synopsis>When set, Asterisk will dynamically create and destroy a NoOp priority 1 extension for a given
- peer who registers or unregisters with us.</synopsis>
- </configOption>
+ <synopsis>When set, Asterisk will dynamically create and destroy a NoOp priority 1 extension for a given
+ peer who registers or unregisters with us.</synopsis>
+ </configOption>
<configOption name="default_outbound_endpoint" default="default_outbound_endpoint">
<synopsis>Endpoint to use when sending an outbound request to a URI without a specified endpoint.</synopsis>
</configOption>
@@ -1439,15 +1462,15 @@
</configOption>
<configOption name="debug" default="no">
<synopsis>Enable/Disable SIP debug logging. Valid options include yes|no or
- a host address</synopsis>
+ 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_*).
- You can use the CLI command "pjsip show identifiers" to see the
- identifiers currently available.</synopsis>
- <description>
+ Identifier names are usually derived from and can be found in the endpoint
+ identifier module itself (res_pjsip_endpoint_identifier_*).
+ You can use the CLI command "pjsip show identifiers" to see the
+ identifiers currently available.</synopsis>
+ <description>
<note><para>
One of the identifiers is "auth_username" which matches on the username in
an Authentication header. This method has some security considerations because an
@@ -1461,17 +1484,59 @@
how many unmatched requests are received from a single ip address before a security
event is generated using the unidentified_request parameters.
</para></note>
- </description>
+ </description>
</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>
+ set to this value if there is no better option (such as CallerID) to be
+ used.</synopsis>
</configOption>
<configOption name="default_realm" default="asterisk">
<synopsis>When Asterisk generates an challenge, the digest will be
- set to this value if there is no better option (such as auth/realm) to be
- used.</synopsis>
+ set to this value if there is no better option (such as auth/realm) to be
+ used.</synopsis>
+ </configOption>
+ <configOption name="mwi_tps_queue_high" default="500">
+ <synopsis>MWI taskprocessor high water alert trigger level.</synopsis>
+ <description>
+ <para>On a heavily loaded system you may need to adjust the
+ taskprocessor queue limits. If any taskprocessor queue size
+ reaches its high water level then pjsip will stop processing
+ new requests until the alert is cleared. The alert clears
+ when all alerting taskprocessor queues have dropped to their
+ low water clear level.
+ </para>
+ </description>
+ </configOption>
+ <configOption name="mwi_tps_queue_low" default="-1">
+ <synopsis>MWI taskprocessor low water clear alert level.</synopsis>
+ <description>
+ <para>On a heavily loaded system you may need to adjust the
+ taskprocessor queue limits. If any taskprocessor queue size
+ reaches its high water level then pjsip will stop processing
+ new requests until the alert is cleared. The alert clears
+ when all alerting taskprocessor queues have dropped to their
+ low water clear level.
+ </para>
+ <note><para>Set to -1 for the low water level to be 90% of
+ the high water level.</para></note>
+ </description>
+ </configOption>
+ <configOption name="mwi_disable_initial_unsolicited" default="no">
+ <synopsis>Enable/Disable sending unsolicited MWI to all endpoints on startup.</synopsis>
+ <description>
+ <para>When the initial unsolicited MWI notification are
+ enabled on startup then the initial notifications
+ get sent at startup. If you have a lot of endpoints
+ (thousands) that use unsolicited MWI then you may
+ want to consider disabling the initial startup
+ notifications.
+ </para>
+ <para>When the initial unsolicited MWI notifications are
+ disabled on startup then the notifications will start
+ on the endpoint's next contact update.
+ </para>
+ </description>
</configOption>
</configObject>
</configFile>
@@ -1951,6 +2016,9 @@
<parameter name="ActiveChannels">
<para>The number of active channels associated with this endpoint.</para>
</parameter>
+ <parameter name="SubscribeContext">
+ <para><xi:include xpointer="xpointer(/docs/configInfo[@name='res_pjsip']/configFile[@name='pjsip.conf']/configObject[@name='endpoint']/configOption[@name='subscribe_context']/synopsis/node())"/></para>
+ </parameter>
</syntax>
</managerEventInstance>
</managerEvent>
@@ -2033,7 +2101,7 @@
Provides a listing of all endpoints. For each endpoint an <literal>EndpointList</literal> event
is raised that contains relevant attributes and status information. Once all
endpoints have been listed an <literal>EndpointListComplete</literal> event is issued.
- </para>
+ </para>
</description>
<responses>
<list-elements>
@@ -2069,7 +2137,7 @@
<literal>IdentifyDetail</literal>. Some events may be listed multiple times if multiple objects are
associated (for instance AoRs). Once all detail events have been raised a final
<literal>EndpointDetailComplete</literal> event is issued.
- </para>
+ </para>
</description>
<responses>
<list-elements>
@@ -2112,13 +2180,13 @@ static struct ast_threadpool *sip_threadpool;
static pj_sockaddr host_ip_ipv4;
/*! Local host address for IPv4 (string form) */
-static char host_ip_ipv4_string[PJ_INET6_ADDRSTRLEN + 2];
+static char host_ip_ipv4_string[PJ_INET6_ADDRSTRLEN];
/*! 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 char host_ip_ipv6_string[PJ_INET6_ADDRSTRLEN];
static int register_service_noref(void *data)
{
@@ -4099,6 +4167,7 @@ long ast_sip_threadpool_queue_size(void)
return ast_threadpool_queue_size(sip_threadpool);
}
+#ifdef TEST_FRAMEWORK
AST_TEST_DEFINE(xml_sanitization_end_null)
{
char sanitized[8];
@@ -4149,6 +4218,7 @@ AST_TEST_DEFINE(xml_sanitization_exceeds_buffer)
return AST_TEST_PASS;
}
+#endif
/*!
* \internal
@@ -4201,6 +4271,7 @@ static int unload_pjsip(void *data)
static int load_pjsip(void)
{
+ const unsigned int flags = 0; /* no port, no brackets */
pj_status_t status;
/* The third parameter is just copied from
@@ -4225,12 +4296,12 @@ static int load_pjsip(void)
}
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);
+ pj_sockaddr_print(&host_ip_ipv4, host_ip_ipv4_string, sizeof(host_ip_ipv4_string), flags);
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);
+ pj_sockaddr_print(&host_ip_ipv6, host_ip_ipv6_string, sizeof(host_ip_ipv6_string), flags);
ast_verb(3, "Local IPv6 address determined to be: %s\n", host_ip_ipv6_string);
}
@@ -4252,6 +4323,19 @@ error:
return AST_MODULE_LOAD_DECLINE;
}
+/*
+ * This is a place holder function to ensure that pjmedia_strerr() is at
+ * least directly referenced by this module to ensure that the loader
+ * linker will link to the function. If a module only indirectly
+ * references a function from another module, such as a callback parameter
+ * to a function, the loader linker has been known to miss the link.
+ */
+void never_called_res_pjsip(void);
+void never_called_res_pjsip(void)
+{
+ pjmedia_strerror(0, NULL, 0);
+}
+
static int load_module(void)
{
struct ast_threadpool_options options;
@@ -4267,6 +4351,12 @@ static int load_module(void)
goto error;
}
+ /* Register PJMEDIA error codes for SDP parsing errors */
+ if (pj_register_strerror(PJMEDIA_ERRNO_START, PJ_ERRNO_SPACE_SIZE, pjmedia_strerror)
+ != PJ_SUCCESS) {
+ ast_log(LOG_WARNING, "Failed to register pjmedia error codes. Codes will not be decoded.\n");
+ }
+
if (ast_sip_initialize_system()) {
ast_log(LOG_ERROR, "Failed to initialize SIP 'system' configuration section. Aborting load\n");
goto error;
diff --git a/res/res_pjsip/config_global.c b/res/res_pjsip/config_global.c
index 6bb688804..975c5eefe 100644
--- a/res/res_pjsip/config_global.c
+++ b/res/res_pjsip/config_global.c
@@ -24,6 +24,7 @@
#include "asterisk/res_pjsip.h"
#include "include/res_pjsip_private.h"
#include "asterisk/sorcery.h"
+#include "asterisk/taskprocessor.h"
#include "asterisk/ast_version.h"
#include "asterisk/res_pjsip_cli.h"
@@ -43,6 +44,9 @@
#define DEFAULT_UNIDENTIFIED_REQUEST_COUNT 5
#define DEFAULT_UNIDENTIFIED_REQUEST_PERIOD 5
#define DEFAULT_UNIDENTIFIED_REQUEST_PRUNE_INTERVAL 30
+#define DEFAULT_MWI_TPS_QUEUE_HIGH AST_TASKPROCESSOR_HIGH_WATER_LEVEL
+#define DEFAULT_MWI_TPS_QUEUE_LOW -1
+#define DEFAULT_MWI_DISABLE_INITIAL_UNSOLICITED 0
static char default_useragent[256];
@@ -79,6 +83,14 @@ struct global_config {
unsigned int unidentified_request_period;
/* Interval at which expired unidentifed requests will be pruned */
unsigned int unidentified_request_prune_interval;
+ struct {
+ /*! Taskprocessor high water alert trigger level */
+ unsigned int tps_queue_high;
+ /*! Taskprocessor low water clear alert level. */
+ int tps_queue_low;
+ /*! Nonzero to disable sending unsolicited mwi to all endpoints on startup */
+ unsigned int disable_initial_unsolicited;
+ } mwi;
};
static void global_destructor(void *obj)
@@ -106,6 +118,18 @@ static int global_apply(const struct ast_sorcery *sorcery, void *obj)
struct global_config *cfg = obj;
char max_forwards[10];
+ if (ast_strlen_zero(cfg->debug)) {
+ ast_log(LOG_ERROR,
+ "Global option 'debug' can't be empty. Set it to a valid value or remove the entry to accept 'no' as the default\n");
+ return -1;
+ }
+
+ if (ast_strlen_zero(cfg->default_from_user)) {
+ ast_log(LOG_ERROR,
+ "Global option 'default_from_user' can't be empty. Set it to a valid value or remove the entry to accept 'asterisk' as the default\n");
+ return -1;
+ }
+
snprintf(max_forwards, sizeof(max_forwards), "%u", cfg->max_forwards);
ast_sip_add_global_request_header("Max-Forwards", max_forwards, 1);
@@ -314,6 +338,53 @@ void ast_sip_get_default_from_user(char *from_user, size_t size)
}
}
+
+unsigned int ast_sip_get_mwi_tps_queue_high(void)
+{
+ unsigned int tps_queue_high;
+ struct global_config *cfg;
+
+ cfg = get_global_cfg();
+ if (!cfg) {
+ return DEFAULT_MWI_TPS_QUEUE_HIGH;
+ }
+
+ tps_queue_high = cfg->mwi.tps_queue_high;
+ ao2_ref(cfg, -1);
+ return tps_queue_high;
+}
+
+int ast_sip_get_mwi_tps_queue_low(void)
+{
+ int tps_queue_low;
+ struct global_config *cfg;
+
+ cfg = get_global_cfg();
+ if (!cfg) {
+ return DEFAULT_MWI_TPS_QUEUE_LOW;
+ }
+
+ tps_queue_low = cfg->mwi.tps_queue_low;
+ ao2_ref(cfg, -1);
+ return tps_queue_low;
+}
+
+unsigned int ast_sip_get_mwi_disable_initial_unsolicited(void)
+{
+ unsigned int disable_initial_unsolicited;
+ struct global_config *cfg;
+
+ cfg = get_global_cfg();
+ if (!cfg) {
+ return DEFAULT_MWI_DISABLE_INITIAL_UNSOLICITED;
+ }
+
+ disable_initial_unsolicited = cfg->mwi.disable_initial_unsolicited;
+ ao2_ref(cfg, -1);
+ return disable_initial_unsolicited;
+}
+
+
/*!
* \internal
* \brief Observer to set default global object if none exist.
@@ -450,6 +521,15 @@ int ast_sip_initialize_sorcery_global(void)
OPT_UINT_T, 0, FLDSET(struct global_config, unidentified_request_prune_interval));
ast_sorcery_object_field_register(sorcery, "global", "default_realm", DEFAULT_REALM,
OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, default_realm));
+ ast_sorcery_object_field_register(sorcery, "global", "mwi_tps_queue_high",
+ __stringify(DEFAULT_MWI_TPS_QUEUE_HIGH),
+ OPT_UINT_T, 0, FLDSET(struct global_config, mwi.tps_queue_high));
+ ast_sorcery_object_field_register(sorcery, "global", "mwi_tps_queue_low",
+ __stringify(DEFAULT_MWI_TPS_QUEUE_LOW),
+ OPT_INT_T, 0, FLDSET(struct global_config, mwi.tps_queue_low));
+ ast_sorcery_object_field_register(sorcery, "global", "mwi_disable_initial_unsolicited",
+ DEFAULT_MWI_DISABLE_INITIAL_UNSOLICITED ? "yes" : "no",
+ OPT_BOOL_T, 1, FLDSET(struct global_config, mwi.disable_initial_unsolicited));
if (ast_sorcery_instance_observer_add(sorcery, &observer_callbacks_global)) {
return -1;
diff --git a/res/res_pjsip/config_transport.c b/res/res_pjsip/config_transport.c
index b9208976f..60b4507cd 100644
--- a/res/res_pjsip/config_transport.c
+++ b/res/res_pjsip/config_transport.c
@@ -562,12 +562,13 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
}
} else if (transport->type == AST_TRANSPORT_TCP) {
pjsip_tcp_transport_cfg cfg;
- int option = 1;
+ static int option = 1;
pjsip_tcp_transport_cfg_default(&cfg, temp_state->state->host.addr.sa_family);
cfg.bind_addr = temp_state->state->host;
cfg.async_cnt = transport->async_operations;
set_qos(transport, &cfg.qos_params);
+ /* sockopt_params.options is copied to each newly connected socket */
cfg.sockopt_params.options[0].level = pj_SOL_TCP();
cfg.sockopt_params.options[0].optname = pj_TCP_NODELAY();
cfg.sockopt_params.options[0].optval = &option;
diff --git a/res/res_pjsip/location.c b/res/res_pjsip/location.c
index bf08d8e86..d8f0c58b5 100644
--- a/res/res_pjsip/location.c
+++ b/res/res_pjsip/location.c
@@ -122,6 +122,7 @@ static void *contact_alloc(const char *name)
return NULL;
}
+ ast_string_field_init_extended(contact, endpoint_name);
ast_string_field_init_extended(contact, reg_server);
ast_string_field_init_extended(contact, via_addr);
ast_string_field_init_extended(contact, call_id);
@@ -172,16 +173,15 @@ static int contact_link_static(void *obj, void *arg, int flags)
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);
- struct ast_sip_contact *contact;
+ struct ao2_container *contacts;
+ struct ast_sip_contact *contact = NULL;
contacts = ast_sip_location_retrieve_aor_contacts(aor);
- if (!contacts || (ao2_container_count(contacts) == 0)) {
- return NULL;
+ if (contacts && ao2_container_count(contacts)) {
+ /* Get the first AOR contact in the container. */
+ contact = ao2_callback(contacts, 0, NULL, NULL);
}
-
- /* Get the first AOR contact in the container. */
- contact = ao2_callback(contacts, 0, NULL, NULL);
+ ao2_cleanup(contacts);
return contact;
}
@@ -213,12 +213,12 @@ struct ao2_container *ast_sip_location_retrieve_aor_contacts(const struct ast_si
struct ao2_container *contacts;
struct ast_named_lock *lock;
- lock = ast_named_lock_get(AST_NAMED_LOCK_TYPE_RWLOCK, "aor", ast_sorcery_object_get_id(aor));
+ lock = ast_named_lock_get(AST_NAMED_LOCK_TYPE_MUTEX, "aor", ast_sorcery_object_get_id(aor));
if (!lock) {
return NULL;
}
- ao2_wrlock(lock);
+ ao2_lock(lock);
contacts = ast_sip_location_retrieve_aor_contacts_nolock(aor);
ao2_unlock(lock);
ast_named_lock_put(lock);
@@ -314,14 +314,16 @@ int ast_sip_location_add_contact_nolock(struct ast_sip_aor *aor, const char *uri
const char *via_addr, int via_port, const char *call_id,
struct ast_sip_endpoint *endpoint)
{
+ struct ast_sip_contact *contact;
+ int res;
char name[MAX_OBJECT_FIELD * 2 + 3];
- RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup);
char hash[33];
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))) {
+ contact = ast_sorcery_alloc(ast_sip_get_sorcery(), "contact", name);
+ if (!contact) {
return -1;
}
@@ -357,7 +359,13 @@ int ast_sip_location_add_contact_nolock(struct ast_sip_aor *aor, const char *uri
contact->endpoint = ao2_bump(endpoint);
- return ast_sorcery_create(ast_sip_get_sorcery(), contact);
+ if (endpoint) {
+ ast_string_field_set(contact, endpoint_name, ast_sorcery_object_get_id(endpoint));
+ }
+
+ res = ast_sorcery_create(ast_sip_get_sorcery(), contact);
+ ao2_ref(contact, -1);
+ return res;
}
int ast_sip_location_add_contact(struct ast_sip_aor *aor, const char *uri,
@@ -368,12 +376,12 @@ int ast_sip_location_add_contact(struct ast_sip_aor *aor, const char *uri,
int res;
struct ast_named_lock *lock;
- lock = ast_named_lock_get(AST_NAMED_LOCK_TYPE_RWLOCK, "aor", ast_sorcery_object_get_id(aor));
+ lock = ast_named_lock_get(AST_NAMED_LOCK_TYPE_MUTEX, "aor", ast_sorcery_object_get_id(aor));
if (!lock) {
return -1;
}
- ao2_wrlock(lock);
+ ao2_lock(lock);
res = ast_sip_location_add_contact_nolock(aor, uri, expiration_time, path_info, user_agent,
via_addr, via_port, call_id,
endpoint);
@@ -598,7 +606,9 @@ static int voicemail_extension_to_str(const void *obj, const intptr_t *args, cha
int ast_sip_for_each_aor(const char *aors, ao2_callback_fn on_aor, void *arg)
{
- char *copy, *name;
+ char *copy;
+ char *name;
+ int res;
if (!on_aor || ast_strlen_zero(aors)) {
return 0;
@@ -606,15 +616,15 @@ int ast_sip_for_each_aor(const char *aors, ao2_callback_fn on_aor, void *arg)
copy = ast_strdupa(aors);
while ((name = ast_strip(strsep(&copy, ",")))) {
- RAII_VAR(struct ast_sip_aor *, aor,
- ast_sip_location_retrieve_aor(name), ao2_cleanup);
+ struct ast_sip_aor *aor;
- if (!aor) {
- continue;
- }
-
- if (on_aor(aor, arg, 0)) {
- return -1;
+ aor = ast_sip_location_retrieve_aor(name);
+ if (aor) {
+ res = on_aor(aor, arg, 0);
+ ao2_ref(aor, -1);
+ if (res) {
+ return -1;
+ }
}
}
return 0;
@@ -623,15 +633,16 @@ int ast_sip_for_each_aor(const char *aors, ao2_callback_fn on_aor, void *arg)
static void contact_wrapper_destroy(void *obj)
{
struct ast_sip_contact_wrapper *wrapper = obj;
+
ast_free(wrapper->aor_id);
ast_free(wrapper->contact_id);
- ao2_ref(wrapper->contact, -1);
+ ao2_cleanup(wrapper->contact);
}
int ast_sip_for_each_contact(const struct ast_sip_aor *aor,
ao2_callback_fn on_contact, void *arg)
{
- RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
+ struct ao2_container *contacts;
struct ao2_iterator i;
int res = 0;
void *object = NULL;
@@ -647,7 +658,8 @@ int ast_sip_for_each_contact(const struct ast_sip_aor *aor,
RAII_VAR(struct ast_sip_contact_wrapper *, wrapper, NULL, ao2_cleanup);
const char *aor_id = ast_sorcery_object_get_id(aor);
- wrapper = ao2_alloc(sizeof(struct ast_sip_contact_wrapper), contact_wrapper_destroy);
+ wrapper = ao2_alloc_options(sizeof(struct ast_sip_contact_wrapper),
+ contact_wrapper_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK);
if (!wrapper) {
res = -1;
break;
@@ -671,6 +683,7 @@ int ast_sip_for_each_contact(const struct ast_sip_aor *aor,
}
}
ao2_iterator_destroy(&i);
+ ao2_ref(contacts, -1);
return res;
}
@@ -686,10 +699,11 @@ int ast_sip_contact_to_str(void *object, void *arg, int flags)
static int sip_aor_to_ami(const struct ast_sip_aor *aor, struct ast_str **buf)
{
- RAII_VAR(struct ast_variable *, objset, ast_sorcery_objectset_create2(
- ast_sip_get_sorcery(), aor, AST_HANDLER_ONLY_STRING), ast_variables_destroy);
+ struct ast_variable *objset;
struct ast_variable *i;
+ objset = ast_sorcery_objectset_create2(ast_sip_get_sorcery(), aor,
+ AST_HANDLER_ONLY_STRING);
if (!objset) {
return -1;
}
@@ -701,6 +715,7 @@ static int sip_aor_to_ami(const struct ast_sip_aor *aor, struct ast_str **buf)
for (i = objset; i; i = i->next) {
char *camel = ast_to_camel_case(i->name);
+
if (strcmp(camel, "Contact") == 0) {
ast_free(camel);
camel = NULL;
@@ -709,23 +724,28 @@ static int sip_aor_to_ami(const struct ast_sip_aor *aor, struct ast_str **buf)
ast_free(camel);
}
+ ast_variables_destroy(objset);
return 0;
}
static int contacts_to_str(const void *obj, const intptr_t *args, char **buf)
{
const struct ast_sip_aor *aor = obj;
- RAII_VAR(struct ast_str *, str, ast_str_create(MAX_OBJECT_FIELD), ast_free);
+ struct ast_str *str;
+
+ str = ast_str_create(MAX_OBJECT_FIELD);
+ if (!str) {
+ *buf = NULL;
+ return -1;
+ }
ast_sip_for_each_contact(aor, ast_sip_contact_to_str, &str);
ast_str_truncate(str, -1);
*buf = ast_strdup(ast_str_buffer(str));
- if (!*buf) {
- return -1;
- }
+ ast_free(str);
- return 0;
+ return *buf ? 0 : -1;
}
static int format_ami_aor_handler(void *obj, void *arg, int flags)
@@ -733,17 +753,20 @@ static int format_ami_aor_handler(void *obj, void *arg, int flags)
struct ast_sip_aor *aor = obj;
struct ast_sip_ami *ami = arg;
const struct ast_sip_endpoint *endpoint = ami->arg;
- RAII_VAR(struct ast_str *, buf,
- ast_sip_create_ami_event("AorDetail", ami), ast_free);
-
+ struct ast_str *buf;
+ struct ao2_container *contacts;
int total_contacts;
int num_permanent;
- RAII_VAR(struct ao2_container *, contacts,
- ast_sip_location_retrieve_aor_contacts(aor), ao2_cleanup);
+ buf = ast_sip_create_ami_event("AorDetail", ami);
if (!buf) {
return -1;
}
+ contacts = ast_sip_location_retrieve_aor_contacts(aor);
+ if (!contacts) {
+ ast_free(buf);
+ return -1;
+ }
sip_aor_to_ami(aor, &buf);
total_contacts = ao2_container_count(contacts);
@@ -759,6 +782,8 @@ static int format_ami_aor_handler(void *obj, void *arg, int flags)
astman_append(ami->s, "%s\r\n", ast_str_buffer(buf));
ami->count++;
+ ast_free(buf);
+ ao2_ref(contacts, -1);
return 0;
}
@@ -776,7 +801,7 @@ struct ast_sip_endpoint_formatter endpoint_aor_formatter = {
static struct ao2_container *cli_aor_get_container(const char *regex)
{
- RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup);
+ struct ao2_container *container;
struct ao2_container *s_container;
container = ast_sorcery_retrieve_by_regex(ast_sip_get_sorcery(), "aor", regex);
@@ -784,16 +809,15 @@ static struct ao2_container *cli_aor_get_container(const char *regex)
return NULL;
}
+ /* Create a sorted container of aors. */
s_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0,
ast_sorcery_object_id_sort, ast_sorcery_object_id_compare);
- if (!s_container) {
- return NULL;
- }
-
- if (ao2_container_dup(s_container, container, 0)) {
+ if (s_container
+ && ao2_container_dup(s_container, container, 0)) {
ao2_ref(s_container, -1);
- return NULL;
+ s_container = NULL;
}
+ ao2_ref(container, -1);
return s_container;
}
@@ -895,7 +919,7 @@ static struct ao2_container *cli_contact_get_container(const char *regex)
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;
}
@@ -922,9 +946,17 @@ static struct ao2_container *cli_contact_get_container(const char *regex)
static void *cli_contact_retrieve_by_id(const char *id)
{
- RAII_VAR(struct ao2_container *, container, cli_contact_get_container(""), ao2_cleanup);
+ struct ao2_container *container;
+ void *obj;
+
+ container = cli_contact_get_container("");
+ if (!container) {
+ return NULL;
+ }
- return ao2_find(container, id, OBJ_KEY | OBJ_NOLOCK);
+ obj = ao2_find(container, id, OBJ_SEARCH_KEY);
+ ao2_ref(container, -1);
+ return obj;
}
static int cli_contact_print_header(void *obj, void *arg, int flags)
@@ -951,14 +983,13 @@ static int cli_contact_print_body(void *obj, void *arg, int flags)
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, contact_id),
- ao2_cleanup);
+ struct ast_sip_contact_status *status;
ast_assert(contact->uri != NULL);
ast_assert(context->output_buffer != NULL);
+ status = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), CONTACT_STATUS, contact_id);
+
indent = CLI_INDENT_TO_SPACES(context->indent_level);
flexwidth = CLI_LAST_TABSTOP - indent - 9 - strlen(contact->aor) + 1;
@@ -972,6 +1003,7 @@ static int cli_contact_print_body(void *obj, void *arg, int flags)
ast_sip_get_contact_short_status_label(status ? status->status : UNKNOWN),
(status && (status->status != UNKNOWN) ? ((long long) status->rtt) / 1000.0 : NAN));
+ ao2_cleanup(status);
return 0;
}
@@ -995,8 +1027,6 @@ static const char *cli_aor_get_id(const void *obj)
static int cli_aor_print_header(void *obj, void *arg, int flags)
{
struct ast_sip_cli_context *context = arg;
- RAII_VAR(struct ast_sip_cli_formatter_entry *, formatter_entry, NULL, ao2_cleanup);
-
int indent = CLI_INDENT_TO_SPACES(context->indent_level);
int filler = CLI_LAST_TABSTOP - indent - 7;
@@ -1007,10 +1037,13 @@ static int cli_aor_print_header(void *obj, void *arg, int flags)
indent, "Aor", filler, filler, CLI_HEADER_FILLER);
if (context->recurse) {
+ struct ast_sip_cli_formatter_entry *formatter_entry;
+
context->indent_level++;
formatter_entry = ast_sip_lookup_cli_formatter("contact");
if (formatter_entry) {
formatter_entry->print_header(NULL, context, 0);
+ ao2_ref(formatter_entry, -1);
}
context->indent_level--;
}
@@ -1022,7 +1055,6 @@ static int cli_aor_print_body(void *obj, void *arg, int flags)
{
struct ast_sip_aor *aor = obj;
struct ast_sip_cli_context *context = arg;
- RAII_VAR(struct ast_sip_cli_formatter_entry *, formatter_entry, NULL, ao2_cleanup);
int indent;
int flexwidth;
@@ -1040,11 +1072,14 @@ static int cli_aor_print_body(void *obj, void *arg, int flags)
ast_sorcery_object_get_id(aor), aor->max_contacts);
if (context->recurse) {
+ struct ast_sip_cli_formatter_entry *formatter_entry;
+
context->indent_level++;
formatter_entry = ast_sip_lookup_cli_formatter("contact");
if (formatter_entry) {
formatter_entry->iterate(aor, formatter_entry->print_body, context);
+ ao2_ref(formatter_entry, -1);
}
context->indent_level--;
@@ -1138,9 +1173,10 @@ int ast_sip_initialize_sorcery_location(void)
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);
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", "authenticate_qualify", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_contact, authenticate_qualify));
+ ast_sorcery_object_field_register(sorcery, "contact", "authenticate_qualify", "no", OPT_YESNO_T, 1, FLDSET(struct ast_sip_contact, authenticate_qualify));
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));
+ ast_sorcery_object_field_register(sorcery, "contact", "endpoint", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, endpoint_name));
ast_sorcery_object_field_register(sorcery, "contact", "reg_server", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, reg_server));
ast_sorcery_object_field_register(sorcery, "contact", "via_addr", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, via_addr));
ast_sorcery_object_field_register(sorcery, "contact", "via_port", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_contact, via_port));
diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c
index 368f0d8a2..9e757e230 100644
--- a/res/res_pjsip/pjsip_configuration.c
+++ b/res/res_pjsip/pjsip_configuration.c
@@ -58,64 +58,15 @@ static int persistent_endpoint_cmp(void *obj, void *arg, int flags)
return !strcmp(ast_endpoint_get_resource(persistent1->endpoint), id) ? CMP_MATCH | CMP_STOP : 0;
}
-/*! \brief Callback function for changing the state of an endpoint */
-static int persistent_endpoint_update_state(void *obj, void *arg, int flags)
+/*! \brief Internal function for changing the state of an endpoint */
+static void endpoint_update_state(struct ast_endpoint *endpoint, enum ast_endpoint_state state)
{
- struct sip_persistent_endpoint *persistent = obj;
- 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;
char *regcontext;
- 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;
- }
-
- 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;
+ return;
}
regcontext = ast_sip_get_regcontext();
@@ -152,6 +103,83 @@ static int persistent_endpoint_update_state(void *obj, void *arg, int flags)
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));
+}
+
+static void endpoint_publish_contact_status(struct ast_endpoint *endpoint, struct ast_sip_contact_status *contact)
+{
+ struct ast_json *blob;
+ char rtt[32];
+
+ snprintf(rtt, sizeof(rtt), "%" PRId64, contact->rtt);
+ blob = ast_json_pack("{s: s, s: s, s: s, s: s, s: s}",
+ "contact_status", ast_sip_get_contact_status_label(contact->status),
+ "aor", contact->aor,
+ "uri", contact->uri,
+ "roundtrip_usec", rtt,
+ "endpoint_name", ast_endpoint_get_resource(endpoint));
+ if (blob) {
+ ast_endpoint_blob_publish(endpoint, ast_endpoint_contact_state_type(), blob);
+ ast_json_unref(blob);
+ }
+}
+
+/*! \brief Callback function for publishing the status of an endpoint */
+static int persistent_endpoint_publish_status(void *obj, void *arg, int flags)
+{
+ struct sip_persistent_endpoint *persistent = obj;
+ struct ast_endpoint *endpoint = persistent->endpoint;
+ struct ast_sip_contact_status *status = arg;
+
+ /* If the status' aor isn't one of the endpoint's, we skip */
+ if (!strstr(persistent->aors, status->aor)) {
+ return 0;
+ }
+
+ endpoint_publish_contact_status(endpoint, status);
+ return 0;
+}
+
+/*! \brief Callback function for changing the state of an endpoint */
+static int persistent_endpoint_update_state(void *obj, void *arg, int flags)
+{
+ struct sip_persistent_endpoint *persistent = obj;
+ struct ast_endpoint *endpoint = persistent->endpoint;
+ struct ast_sip_contact_status *status = arg;
+ struct ao2_container *contacts;
+ struct ao2_iterator iter;
+ struct ast_sip_contact *contact;
+ enum ast_endpoint_state state = AST_ENDPOINT_OFFLINE;
+
+ /* If the status' aor isn't one of the endpoint's, we skip */
+ if (!strstr(persistent->aors, status->aor)) {
+ return 0;
+ }
+
+ endpoint_publish_contact_status(endpoint, status);
+
+ /* 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) {
+ iter = ao2_iterator_init(contacts, 0);
+ while (state == AST_ENDPOINT_OFFLINE && (contact = ao2_iterator_next(&iter))) {
+ 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(&iter);
+ ao2_ref(contacts, -1);
+ }
+
+ endpoint_update_state(endpoint, state);
return 0;
}
@@ -176,7 +204,7 @@ static void persistent_endpoint_contact_created_observer(const void *object)
}
contact_status->status = CREATED;
- ast_verb(2, "Contact %s/%s has been created\n",contact->aor, contact->uri);
+ ast_verb(2, "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);
@@ -190,7 +218,7 @@ static void persistent_endpoint_contact_deleted_observer(const void *object)
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",
+ ast_log(LOG_ERROR, "Unable to find ast_sip_contact_status for contact %s/%s\n",
contact->aor, contact->uri);
return;
}
@@ -217,13 +245,21 @@ static void persistent_endpoint_contact_status_observer(const void *object)
{
struct ast_sip_contact_status *contact_status = (struct ast_sip_contact_status *)object;
+ if (contact_status->refresh) {
+ /* We are only re-publishing the contact status. */
+ ao2_callback(persistent_endpoints, OBJ_NODATA,
+ persistent_endpoint_publish_status, contact_status);
+ return;
+ }
+
/* 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(3, "Contact %s/%s is now %s. RTT: %.3f msec\n", contact_status->aor, contact_status->uri,
+ ast_verb(3, "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);
@@ -234,19 +270,23 @@ static void persistent_endpoint_contact_status_observer(const void *object)
ast_test_suite_event_notify("AOR_CONTACT_UPDATE",
"Contact: %s\r\n"
- "Status: %s",
+ "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);
+ 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->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));
+ 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 */
@@ -258,7 +298,8 @@ 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);
+ 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 = {
@@ -1184,29 +1225,48 @@ static void persistent_endpoint_destroy(void *obj)
ast_free(persistent->aors);
}
+int ast_sip_persistent_endpoint_update_state(const char *endpoint_name, enum ast_endpoint_state state)
+{
+ struct sip_persistent_endpoint *persistent;
+
+ ao2_lock(persistent_endpoints);
+ persistent = ao2_find(persistent_endpoints, endpoint_name, OBJ_SEARCH_KEY | OBJ_NOLOCK);
+ if (persistent) {
+ endpoint_update_state(persistent->endpoint, state);
+ ao2_ref(persistent, -1);
+ }
+ ao2_unlock(persistent_endpoints);
+ return persistent ? 0 : -1;
+}
+
/*! \brief Internal function which finds (or creates) persistent endpoint information */
static struct ast_endpoint *persistent_endpoint_find_or_create(const struct ast_sip_endpoint *endpoint)
{
RAII_VAR(struct sip_persistent_endpoint *, persistent, NULL, ao2_cleanup);
SCOPED_AO2LOCK(lock, persistent_endpoints);
- if (!(persistent = ao2_find(persistent_endpoints, ast_sorcery_object_get_id(endpoint), OBJ_KEY | OBJ_NOLOCK))) {
- if (!(persistent = ao2_alloc(sizeof(*persistent), persistent_endpoint_destroy))) {
+ persistent = ao2_find(persistent_endpoints, ast_sorcery_object_get_id(endpoint),
+ OBJ_SEARCH_KEY | OBJ_NOLOCK);
+ if (!persistent) {
+ persistent = ao2_alloc_options(sizeof(*persistent), persistent_endpoint_destroy,
+ AO2_ALLOC_OPT_LOCK_NOLOCK);
+ if (!persistent) {
return NULL;
}
- if (!(persistent->endpoint = ast_endpoint_create("PJSIP", ast_sorcery_object_get_id(endpoint)))) {
+ persistent->endpoint = ast_endpoint_create("PJSIP",
+ ast_sorcery_object_get_id(endpoint));
+ if (!persistent->endpoint) {
return NULL;
}
persistent->aors = ast_strdup(endpoint->aors);
-
- if (ast_strlen_zero(persistent->aors)) {
- ast_endpoint_set_state(persistent->endpoint, AST_ENDPOINT_UNKNOWN);
- } else {
- persistent_endpoint_update_state(persistent, NULL, 0);
+ if (!persistent->aors) {
+ return NULL;
}
+ ast_endpoint_set_state(persistent->endpoint, AST_ENDPOINT_UNKNOWN);
+
ao2_link_flags(persistent_endpoints, persistent, OBJ_NOLOCK);
}
@@ -1686,6 +1746,22 @@ static struct ast_cli_entry cli_commands[] = {
struct ast_sip_cli_formatter_entry *channel_formatter;
struct ast_sip_cli_formatter_entry *endpoint_formatter;
+static int on_load_endpoint(void *obj, void *arg, int flags)
+{
+ return sip_endpoint_apply_handler(sip_sorcery, obj);
+}
+
+static void load_all_endpoints(void)
+{
+ struct ao2_container *endpoints;
+
+ endpoints = ast_sorcery_retrieve_by_fields(sip_sorcery, "endpoint", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+ if (endpoints) {
+ ao2_callback(endpoints, OBJ_NODATA, on_load_endpoint, NULL);
+ ao2_ref(endpoints, -1);
+ }
+}
+
int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_module_info)
{
if (ast_manager_register_xml(AMI_SHOW_ENDPOINTS, EVENT_FLAG_SYSTEM, ami_show_endpoints) ||
@@ -1693,7 +1769,9 @@ int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_mod
return -1;
}
- if (!(persistent_endpoints = ao2_container_alloc(PERSISTENT_BUCKETS, persistent_endpoint_hash, persistent_endpoint_cmp))) {
+ persistent_endpoints = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0,
+ PERSISTENT_BUCKETS, persistent_endpoint_hash, NULL, persistent_endpoint_cmp);
+ if (!persistent_endpoints) {
return -1;
}
@@ -1785,6 +1863,7 @@ int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_mod
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "t38_udptl_ec", "none", t38udptl_ec_handler, t38udptl_ec_to_str, NULL, 0, 0);
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "t38_udptl_maxdatagram", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, media.t38.maxdatagram));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "fax_detect", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, faxdetect));
+ ast_sorcery_object_field_register(sip_sorcery, "endpoint", "fax_detect_timeout", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, faxdetect_timeout));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "t38_udptl_nat", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.t38.nat));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "t38_udptl_ipv6", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.t38.ipv6));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "tone_zone", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, zone));
@@ -1827,6 +1906,7 @@ int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_mod
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "contact_deny", "", endpoint_acl_handler, NULL, NULL, 0, 0);
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "contact_permit", "", endpoint_acl_handler, NULL, NULL, 0, 0);
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "contact_acl", "", endpoint_acl_handler, contact_acl_to_str, NULL, 0, 0);
+ ast_sorcery_object_field_register(sip_sorcery, "endpoint", "subscribe_context", "", OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct ast_sip_endpoint, subscription.context));
if (ast_sip_initialize_sorcery_transport()) {
ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n");
@@ -1887,6 +1967,8 @@ int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_mod
ast_sorcery_load(sip_sorcery);
+ load_all_endpoints();
+
return 0;
}
@@ -1910,6 +1992,7 @@ void ast_res_pjsip_destroy_configuration(void)
ast_sip_unregister_cli_formatter(endpoint_formatter);
ast_sip_destroy_cli();
ao2_cleanup(persistent_endpoints);
+ persistent_endpoints = NULL;
}
int ast_res_pjsip_reload_configuration(void)
diff --git a/res/res_pjsip/pjsip_distributor.c b/res/res_pjsip/pjsip_distributor.c
index e8ed89361..8a9119639 100644
--- a/res/res_pjsip/pjsip_distributor.c
+++ b/res/res_pjsip/pjsip_distributor.c
@@ -965,7 +965,7 @@ static int clean_task(const void *data)
static void global_loaded(const char *object_type)
{
char *identifier_order = ast_sip_get_endpoint_identifier_order();
- char *io_copy = ast_strdupa(identifier_order);
+ char *io_copy = identifier_order ? ast_strdupa(identifier_order) : NULL;
char *identify_method;
ast_free(identifier_order);
@@ -982,6 +982,7 @@ static void global_loaded(const char *object_type)
/* Clean out the old task, if any */
ast_sched_clean_by_callback(prune_context, prune_task, clean_task);
+ /* Have to do something with the return value to shut up the stupid compiler. */
if (ast_sched_add_variable(prune_context, unidentified_prune_interval * 1000, prune_task, NULL, 1) < 0) {
return;
}
diff --git a/res/res_pjsip/pjsip_options.c b/res/res_pjsip/pjsip_options.c
index 70bbaf908..6f7455d50 100644
--- a/res/res_pjsip/pjsip_options.c
+++ b/res/res_pjsip/pjsip_options.c
@@ -43,7 +43,6 @@ static const char *status_map [] = {
[UNKNOWN] = "Unknown",
[CREATED] = "Created",
[REMOVED] = "Removed",
- [UPDATED] = "Updated",
};
static const char *short_status_map [] = {
@@ -52,7 +51,6 @@ static const char *short_status_map [] = {
[UNKNOWN] = "Unknown",
[CREATED] = "Created",
[REMOVED] = "Removed",
- [UPDATED] = "Updated",
};
const char *ast_sip_get_contact_status_label(const enum ast_sip_contact_status_type status)
@@ -160,7 +158,7 @@ struct ast_sip_contact_status *ast_res_pjsip_find_or_create_contact_status(const
* \brief Update an ast_sip_contact_status's elements.
*/
static void update_contact_status(const struct ast_sip_contact *contact,
- enum ast_sip_contact_status_type value)
+ enum ast_sip_contact_status_type value, int is_contact_refresh)
{
RAII_VAR(struct ast_sip_contact_status *, status, NULL, ao2_cleanup);
RAII_VAR(struct ast_sip_contact_status *, update, NULL, ao2_cleanup);
@@ -172,6 +170,26 @@ static void update_contact_status(const struct ast_sip_contact *contact,
return;
}
+ if (is_contact_refresh
+ && status->status == CREATED) {
+ /*
+ * The contact status hasn't been updated since creation
+ * and we don't want to re-send a created status.
+ */
+ if (contact->qualify_frequency
+ || status->rtt_start.tv_sec > 0) {
+ /* Ignore, the status will change soon. */
+ return;
+ }
+
+ /*
+ * Convert to a regular contact status update
+ * because the status may never change.
+ */
+ is_contact_refresh = 0;
+ value = UNKNOWN;
+ }
+
update = ast_sorcery_alloc(ast_sip_get_sorcery(), CONTACT_STATUS,
ast_sorcery_object_get_id(status));
if (!update) {
@@ -185,22 +203,34 @@ static void update_contact_status(const struct ast_sip_contact *contact,
return;
}
- update->last_status = status->status;
- update->status = value;
+ if (is_contact_refresh) {
+ /* Copy everything just to set the refresh flag. */
+ update->status = status->status;
+ update->last_status = status->last_status;
+ update->rtt = status->rtt;
+ update->rtt_start = status->rtt_start;
+ update->refresh = 1;
+ } else {
+ 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 && status->rtt_start.tv_sec > 0 ?
- ast_tvdiff_us(ast_tvnow(), status->rtt_start) : 0;
- update->rtt_start = ast_tv(0, 0);
+ /*
+ * if the contact is available calculate the rtt as
+ * the diff between the last start time and "now"
+ */
+ update->rtt = update->status == AVAILABLE && status->rtt_start.tv_sec > 0
+ ? ast_tvdiff_us(ast_tvnow(), status->rtt_start)
+ : 0;
+ update->rtt_start = ast_tv(0, 0);
- ast_test_suite_event_notify("AOR_CONTACT_QUALIFY_RESULT",
- "Contact: %s\r\n"
- "Status: %s\r\n"
- "RTT: %" PRId64,
- ast_sorcery_object_get_id(update),
- ast_sip_get_contact_status_label(update->status),
- update->rtt);
+ 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",
@@ -317,10 +347,10 @@ static void qualify_contact_cb(void *token, pjsip_event *e)
/* Fall through */
case PJSIP_EVENT_TRANSPORT_ERROR:
case PJSIP_EVENT_TIMER:
- update_contact_status(contact, UNAVAILABLE);
+ update_contact_status(contact, UNAVAILABLE, 0);
break;
case PJSIP_EVENT_RX_MSG:
- update_contact_status(contact, AVAILABLE);
+ update_contact_status(contact, AVAILABLE, 0);
break;
}
ao2_cleanup(contact);
@@ -341,7 +371,12 @@ static int qualify_contact(struct ast_sip_endpoint *endpoint, struct ast_sip_con
if (endpoint) {
endpoint_local = ao2_bump(endpoint);
} else {
- endpoint_local = find_an_endpoint(contact);
+ if (!ast_strlen_zero(contact->endpoint_name)) {
+ endpoint_local = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", contact->endpoint_name);
+ }
+ if (!endpoint_local) {
+ endpoint_local = find_an_endpoint(contact);
+ }
if (!endpoint_local) {
ast_log(LOG_ERROR, "Unable to find an endpoint to qualify contact %s\n",
contact->uri);
@@ -371,7 +406,7 @@ static int qualify_contact(struct ast_sip_endpoint *endpoint, struct ast_sip_con
!= PJ_SUCCESS) {
ast_log(LOG_ERROR, "Unable to send request to qualify contact %s\n",
contact->uri);
- update_contact_status(contact, UNAVAILABLE);
+ update_contact_status(contact, UNAVAILABLE, 0);
ao2_ref(contact, -1);
return -1;
}
@@ -531,7 +566,7 @@ static void qualify_and_schedule(struct ast_sip_contact *contact)
schedule_qualify(contact, contact->qualify_frequency * 1000);
} else {
- update_contact_status(contact, UNKNOWN);
+ update_contact_status(contact, UNKNOWN, 0);
}
}
@@ -550,8 +585,7 @@ static void contact_created(const void *obj)
*/
static void contact_updated(const void *obj)
{
- update_contact_status((struct ast_sip_contact *) obj, UPDATED);
- qualify_and_schedule((struct ast_sip_contact *) obj);
+ update_contact_status(obj, AVAILABLE, 1);
}
/*!
@@ -580,8 +614,8 @@ static void contact_deleted(const void *obj)
static const struct ast_sorcery_observer contact_observer = {
.created = contact_created,
+ .updated = contact_updated,
.deleted = contact_deleted,
- .updated = contact_updated
};
static pj_bool_t options_start(void)
@@ -1057,7 +1091,7 @@ static void qualify_and_schedule_contact(struct ast_sip_contact *contact)
if (contact->qualify_frequency) {
schedule_qualify(contact, initial_interval);
} else {
- update_contact_status(contact, UNKNOWN);
+ update_contact_status(contact, UNKNOWN, 0);
}
}
@@ -1240,7 +1274,7 @@ static void aor_observer_deleted(const void *obj)
contacts = ast_sip_location_retrieve_aor_contacts(aor);
if (contacts) {
- ao2_callback(contacts, OBJ_NODATA, unschedule_contact_cb, NULL);
+ ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, unschedule_contact_cb, NULL);
ao2_ref(contacts, -1);
}
}
@@ -1251,6 +1285,126 @@ static const struct ast_sorcery_observer observer_callbacks_options = {
.deleted = aor_observer_deleted
};
+static int aor_update_endpoint_state(void *obj, void *arg, int flags)
+{
+ struct ast_sip_endpoint *endpoint = obj;
+ const char *endpoint_name = ast_sorcery_object_get_id(endpoint);
+ char *aor = arg;
+ char *endpoint_aor;
+ char *endpoint_aors;
+
+ if (ast_strlen_zero(aor) || ast_strlen_zero(endpoint->aors)) {
+ return 0;
+ }
+
+ endpoint_aors = ast_strdupa(endpoint->aors);
+ while ((endpoint_aor = ast_strip(strsep(&endpoint_aors, ",")))) {
+ if (!strcmp(aor, endpoint_aor)) {
+ if (ast_sip_persistent_endpoint_update_state(endpoint_name, AST_ENDPOINT_ONLINE) == -1) {
+ ast_log(LOG_WARNING, "Unable to find persistent endpoint '%s' for aor '%s'\n",
+ endpoint_name, aor);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int on_aor_update_endpoint_state(void *obj, void *arg, int flags)
+{
+ struct ast_sip_aor *aor = obj;
+ struct ao2_container *endpoints;
+ RAII_VAR(struct ast_variable *, var, NULL, ast_variables_destroy);
+ const char *aor_name = ast_sorcery_object_get_id(aor);
+ char *aor_like;
+
+ if (ast_strlen_zero(aor_name)) {
+ return -1;
+ }
+
+ if (aor->permanent_contacts && ((int)(aor->qualify_frequency * 1000)) <= 0) {
+ aor_like = ast_alloca(strlen(aor_name) + 3);
+ sprintf(aor_like, "%%%s%%", aor_name);
+ var = ast_variable_new("aors LIKE", aor_like, "");
+ if (!var) {
+ return -1;
+ }
+ endpoints = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(),
+ "endpoint", AST_RETRIEVE_FLAG_MULTIPLE, var);
+
+ if (endpoints) {
+ /*
+ * Because aors are a string list, we have to use a pattern match but since a simple
+ * pattern match could return an endpoint that has an aor of "aaabccc" when searching
+ * for "abc", we still have to iterate over them to find an exact aor match.
+ */
+ ao2_callback(endpoints, 0, aor_update_endpoint_state, (char *)aor_name);
+ ao2_ref(endpoints, -1);
+ }
+ }
+
+ return 0;
+}
+
+static int contact_update_endpoint_state(void *obj, void *arg, int flags)
+{
+ const struct ast_sip_contact *contact = obj;
+ struct timeval tv = ast_tvnow();
+
+ if (!ast_strlen_zero(contact->endpoint_name) && ((int)(contact->qualify_frequency * 1000)) <= 0 &&
+ contact->expiration_time.tv_sec > tv.tv_sec) {
+
+ if (ast_sip_persistent_endpoint_update_state(contact->endpoint_name, AST_ENDPOINT_ONLINE) == -1) {
+ ast_log(LOG_WARNING, "Unable to find persistent endpoint '%s' for contact '%s/%s'\n",
+ contact->endpoint_name, contact->aor, contact->uri);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static void update_all_unqualified_endpoints(void)
+{
+ struct ao2_container *aors;
+ struct ao2_container *contacts;
+ RAII_VAR(struct ast_variable *, var_aor, NULL, ast_variables_destroy);
+ RAII_VAR(struct ast_variable *, var_contact, NULL, ast_variables_destroy);
+ RAII_VAR(char *, time_now, NULL, ast_free);
+ struct timeval tv = ast_tvnow();
+
+ if (!(var_aor = ast_variable_new("contact !=", "", ""))) {
+ return;
+ }
+ if (!(var_aor->next = ast_variable_new("qualify_frequency <=", "0", ""))) {
+ return;
+ }
+
+ if (ast_asprintf(&time_now, "%ld", tv.tv_sec) == -1) {
+ return;
+ }
+ if (!(var_contact = ast_variable_new("expiration_time >", time_now, ""))) {
+ return;
+ }
+ if (!(var_contact->next = ast_variable_new("qualify_frequency <=", "0", ""))) {
+ return;
+ }
+
+ aors = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(),
+ "aor", AST_RETRIEVE_FLAG_MULTIPLE, var_aor);
+ if (aors) {
+ ao2_callback(aors, OBJ_NODATA, on_aor_update_endpoint_state, NULL);
+ ao2_ref(aors, -1);
+ }
+
+ contacts = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(),
+ "contact", AST_RETRIEVE_FLAG_MULTIPLE, var_contact);
+ if (contacts) {
+ ao2_callback(contacts, OBJ_NODATA, contact_update_endpoint_state, NULL);
+ ao2_ref(contacts, -1);
+ }
+}
+
int ast_res_pjsip_init_options_handling(int reload)
{
static const pj_str_t STR_OPTIONS = { "OPTIONS", 7 };
@@ -1292,6 +1446,7 @@ int ast_res_pjsip_init_options_handling(int reload)
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));
+ update_all_unqualified_endpoints();
qualify_and_schedule_all();
return 0;
diff --git a/res/res_pjsip_caller_id.c b/res/res_pjsip_caller_id.c
index efa1b89a8..4d9f09237 100644
--- a/res/res_pjsip_caller_id.c
+++ b/res/res_pjsip_caller_id.c
@@ -413,7 +413,7 @@ static pjsip_fromto_hdr *create_new_id_hdr(const pj_str_t *hdr_name, pjsip_fromt
id_hdr = pjsip_from_hdr_create(tdata->pool);
id_hdr->type = PJSIP_H_OTHER;
pj_strdup(tdata->pool, &id_hdr->name, hdr_name);
- id_hdr->sname.slen = 0;
+ id_hdr->sname = id_hdr->name;
id_name_addr = pjsip_uri_clone(tdata->pool, base->uri);
id_uri = pjsip_uri_get_uri(id_name_addr->uri);
diff --git a/res/res_pjsip_diversion.c b/res/res_pjsip_diversion.c
index 41e6c821a..99d18da44 100644
--- a/res/res_pjsip_diversion.c
+++ b/res/res_pjsip_diversion.c
@@ -305,7 +305,7 @@ static void add_diversion_header(pjsip_tx_data *tdata, struct ast_party_redirect
hdr = pjsip_from_hdr_create(tdata->pool);
hdr->type = PJSIP_H_OTHER;
pj_strdup(tdata->pool, &hdr->name, &diversion_name);
- hdr->sname.slen = 0;
+ hdr->sname = hdr->name;
name_addr = pjsip_uri_clone(tdata->pool, base);
uri = pjsip_uri_get_uri(name_addr->uri);
diff --git a/res/res_pjsip_exten_state.c b/res/res_pjsip_exten_state.c
index 69a458993..73a5af3b0 100644
--- a/res/res_pjsip_exten_state.c
+++ b/res/res_pjsip_exten_state.c
@@ -352,9 +352,11 @@ static void subscription_shutdown(struct ast_sip_subscription *sub)
static int new_subscribe(struct ast_sip_endpoint *endpoint,
const char *resource)
{
- if (!ast_exists_extension(NULL, endpoint->context, resource, PRIORITY_HINT, NULL)) {
+ const char *context = S_OR(endpoint->subscription.context, endpoint->context);
+
+ if (!ast_exists_extension(NULL, context, resource, PRIORITY_HINT, NULL)) {
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);
+ resource, context);
return 404;
}
@@ -372,7 +374,9 @@ static int subscription_established(struct ast_sip_subscription *sip_sub)
return -1;
}
- ast_copy_string(exten_state_sub->context, endpoint->context, sizeof(exten_state_sub->context));
+ ast_copy_string(exten_state_sub->context,
+ S_OR(endpoint->subscription.context, endpoint->context),
+ sizeof(exten_state_sub->context));
ast_copy_string(exten_state_sub->exten, resource, sizeof(exten_state_sub->exten));
if ((exten_state_sub->id = ast_extension_state_add_destroy_extended(
diff --git a/res/res_pjsip_messaging.c b/res/res_pjsip_messaging.c
index 596223293..594c0fdac 100644
--- a/res/res_pjsip_messaging.c
+++ b/res/res_pjsip_messaging.c
@@ -476,6 +476,24 @@ static enum pjsip_status_code rx_data_to_ast_msg(pjsip_rx_data *rdata, struct as
field = pj_sockaddr_print(&rdata->pkt_info.src_addr, buf, sizeof(buf) - 1, 1);
res |= ast_msg_set_var(msg, "PJSIP_RECVADDR", field);
+ switch (rdata->tp_info.transport->key.type) {
+ case PJSIP_TRANSPORT_UDP:
+ case PJSIP_TRANSPORT_UDP6:
+ field = "udp";
+ break;
+ case PJSIP_TRANSPORT_TCP:
+ case PJSIP_TRANSPORT_TCP6:
+ field = "tcp";
+ break;
+ case PJSIP_TRANSPORT_TLS:
+ case PJSIP_TRANSPORT_TLS6:
+ field = "tls";
+ break;
+ default:
+ field = rdata->tp_info.transport->type_name;
+ }
+ ast_msg_set_var(msg, "PJSIP_TRANSPORT", field);
+
if (print_body(rdata, buf, sizeof(buf) - 1) > 0) {
res |= ast_msg_set_body(msg, "%s", buf);
}
diff --git a/res/res_pjsip_mwi.c b/res/res_pjsip_mwi.c
index 9eba335b5..fb6e6b8f6 100644
--- a/res/res_pjsip_mwi.c
+++ b/res/res_pjsip_mwi.c
@@ -35,6 +35,7 @@
#include "asterisk/module.h"
#include "asterisk/logger.h"
#include "asterisk/astobj2.h"
+#include "asterisk/taskprocessor.h"
#include "asterisk/sorcery.h"
#include "asterisk/stasis.h"
#include "asterisk/app.h"
@@ -52,6 +53,12 @@ static char *default_voicemail_extension;
#define MWI_DATASTORE "MWI datastore"
+/*! Number of serializers in pool if one not supplied. */
+#define MWI_SERIALIZER_POOL_SIZE 8
+
+/*! Pool of serializers to use if not supplied. */
+static struct ast_taskprocessor *mwi_serializer_pool[MWI_SERIALIZER_POOL_SIZE];
+
static void mwi_subscription_shutdown(struct ast_sip_subscription *sub);
static void mwi_to_ami(struct ast_sip_subscription *sub, struct ast_str **buf);
static int mwi_new_subscribe(struct ast_sip_endpoint *endpoint,
@@ -119,6 +126,117 @@ struct mwi_subscription {
char id[1];
};
+/*!
+ * \internal
+ * \brief Shutdown the serializers in the mwi pool.
+ * \since 13.12.0
+ *
+ * \return Nothing
+ */
+static void mwi_serializer_pool_shutdown(void)
+{
+ int idx;
+
+ for (idx = 0; idx < MWI_SERIALIZER_POOL_SIZE; ++idx) {
+ ast_taskprocessor_unreference(mwi_serializer_pool[idx]);
+ mwi_serializer_pool[idx] = NULL;
+ }
+}
+
+/*!
+ * \internal
+ * \brief Setup the serializers in the mwi pool.
+ * \since 13.12.0
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int mwi_serializer_pool_setup(void)
+{
+ char tps_name[AST_TASKPROCESSOR_MAX_NAME + 1];
+ int idx;
+
+ for (idx = 0; idx < MWI_SERIALIZER_POOL_SIZE; ++idx) {
+ /* Create name with seq number appended. */
+ ast_taskprocessor_build_name(tps_name, sizeof(tps_name), "pjsip/mwi");
+
+ mwi_serializer_pool[idx] = ast_sip_create_serializer_named(tps_name);
+ if (!mwi_serializer_pool[idx]) {
+ mwi_serializer_pool_shutdown();
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/*!
+ * \internal
+ * \brief Pick a mwi serializer from the pool.
+ * \since 13.12.0
+ *
+ * \retval least queue size task processor.
+ */
+static struct ast_taskprocessor *get_mwi_serializer(void)
+{
+ int idx;
+ int pos;
+
+ if (!mwi_serializer_pool[0]) {
+ return NULL;
+ }
+
+ for (pos = idx = 0; idx < MWI_SERIALIZER_POOL_SIZE; ++idx) {
+ if (ast_taskprocessor_size(mwi_serializer_pool[idx]) < ast_taskprocessor_size(mwi_serializer_pool[pos])) {
+ pos = idx;
+ }
+ }
+
+ return mwi_serializer_pool[pos];
+}
+
+/*!
+ * \internal
+ * \brief Set taskprocessor alert levels for the serializers in the mwi pool.
+ * \since 13.12.0
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int mwi_serializer_set_alert_levels(void)
+{
+ int idx;
+ long tps_queue_high;
+ long tps_queue_low;
+
+ if (!mwi_serializer_pool[0]) {
+ return -1;
+ }
+
+ tps_queue_high = ast_sip_get_mwi_tps_queue_high();
+ if (tps_queue_high <= 0) {
+ ast_log(AST_LOG_WARNING, "Invalid taskprocessor high water alert trigger level '%ld'\n",
+ tps_queue_high);
+ tps_queue_high = AST_TASKPROCESSOR_HIGH_WATER_LEVEL;
+ }
+
+ tps_queue_low = ast_sip_get_mwi_tps_queue_low();
+ if (tps_queue_low < -1 || tps_queue_high < tps_queue_low) {
+ ast_log(AST_LOG_WARNING, "Invalid taskprocessor low water clear alert level '%ld'\n",
+ tps_queue_low);
+ tps_queue_low = -1;
+ }
+
+ for (idx = 0; idx < MWI_SERIALIZER_POOL_SIZE; ++idx) {
+ if (ast_taskprocessor_alert_set_levels(mwi_serializer_pool[idx], tps_queue_low, tps_queue_high)) {
+ ast_log(AST_LOG_WARNING, "Failed to set alert levels for MWI serializer pool #%d.\n",
+ idx);
+ }
+ }
+
+ return 0;
+}
+
+
static void mwi_stasis_cb(void *userdata, struct stasis_subscription *sub,
struct stasis_message *msg);
@@ -630,7 +748,6 @@ static int endpoint_receives_unsolicited_mwi_for_mailbox(struct ast_sip_endpoint
int ret = 0;
mwi_subs = ao2_find(unsolicited_mwi, endpoint_id, OBJ_SEARCH_KEY | OBJ_MULTIPLE);
-
if (!mwi_subs) {
return 0;
}
@@ -945,7 +1062,7 @@ 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;
+ : get_mwi_serializer();
if (ast_sip_push_task(serializer, serialized_notify, ao2_bump(mwi_sub))) {
ao2_ref(mwi_sub, -1);
@@ -971,43 +1088,17 @@ static void mwi_stasis_cb(void *userdata, struct stasis_subscription *sub,
}
}
-/*! \note Called with the unsolicited_mwi conainer lock held. */
+/*! \note Called with the unsolicited_mwi container 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;
- char *endpoint_aors, *aor_name, *mailboxes, *mailbox;
- struct ao2_container *contacts = NULL;
+ char *mailboxes, *mailbox;
if (ast_strlen_zero(endpoint->subscription.mwi.mailboxes)) {
return 0;
}
- endpoint_aors = ast_strdupa(endpoint->aors);
-
- while ((aor_name = ast_strip(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) {
@@ -1089,7 +1180,7 @@ static int send_contact_notify(void *obj, void *arg, int flags)
return 0;
}
- if (ast_sip_push_task(NULL, serialized_notify, ao2_bump(mwi_sub))) {
+ if (ast_sip_push_task(get_mwi_serializer(), serialized_notify, ao2_bump(mwi_sub))) {
ao2_ref(mwi_sub, -1);
}
@@ -1120,7 +1211,8 @@ static void mwi_contact_added(const void *object)
ao2_lock(unsolicited_mwi);
- mwi_subs = ao2_find(unsolicited_mwi, endpoint_id, OBJ_SEARCH_KEY | OBJ_MULTIPLE | OBJ_NOLOCK | OBJ_UNLINK);
+ 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);
@@ -1175,6 +1267,7 @@ static void global_loaded(const char *object_type)
{
ast_free(default_voicemail_extension);
default_voicemail_extension = ast_sip_get_default_voicemail_extension();
+ mwi_serializer_set_alert_levels();
}
static struct ast_sorcery_observer global_observer = {
@@ -1195,21 +1288,29 @@ static int load_module(void)
return AST_MODULE_LOAD_DECLINE;
}
- unsolicited_mwi = ao2_container_alloc(MWI_BUCKETS, mwi_sub_hash, mwi_sub_cmp);
+ if (mwi_serializer_pool_setup()) {
+ ast_log(AST_LOG_WARNING, "Failed to create MWI serializer pool. The default SIP pool will be used for MWI\n");
+ }
+
+ unsolicited_mwi = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, MWI_BUCKETS,
+ mwi_sub_hash, NULL, mwi_sub_cmp);
if (!unsolicited_mwi) {
+ mwi_serializer_pool_shutdown();
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);
ast_sorcery_observer_add(ast_sip_get_sorcery(), "global", &global_observer);
ast_sorcery_reload_object(ast_sip_get_sorcery(), "global");
- 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);
+ if (!ast_sip_get_mwi_disable_initial_unsolicited()) {
+ 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;
@@ -1217,12 +1318,19 @@ static int load_module(void)
static int unload_module(void)
{
- 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(), "global", &global_observer);
ast_sorcery_observer_remove(ast_sip_get_sorcery(), "contact", &mwi_contact_observer);
+
+ ao2_callback(unsolicited_mwi, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, unsubscribe, NULL);
+ ao2_ref(unsolicited_mwi, -1);
+ unsolicited_mwi = NULL;
+
+ mwi_serializer_pool_shutdown();
+
ast_sip_unregister_subscription_handler(&mwi_handler);
+
ast_free(default_voicemail_extension);
+ default_voicemail_extension = NULL;
return 0;
}
diff --git a/res/res_pjsip_outbound_publish.c b/res/res_pjsip_outbound_publish.c
index ab8a6c9bd..35eedf0d2 100644
--- a/res/res_pjsip_outbound_publish.c
+++ b/res/res_pjsip_outbound_publish.c
@@ -31,6 +31,7 @@
#include "asterisk/res_pjsip_outbound_publish.h"
#include "asterisk/module.h"
#include "asterisk/taskprocessor.h"
+#include "asterisk/threadpool.h"
#include "asterisk/datastore.h"
/*** DOCUMENTATION
@@ -161,6 +162,8 @@ struct ast_sip_outbound_publish_client {
struct ast_sip_outbound_publish *publish;
/*! \brief The name of the transport to be used for the publish */
char *transport_name;
+ /*! \brief Serializer for stuff and things */
+ struct ast_taskprocessor *serializer;
};
/*! \brief Outbound publish state information (persists for lifetime of a publish) */
@@ -171,13 +174,11 @@ struct ast_sip_outbound_publish_state {
char id[0];
};
-/*! \brief Unloading data */
-struct unloading_data {
- int is_unloading;
- int count;
- ast_mutex_t lock;
- ast_cond_t cond;
-} unloading;
+/*! 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 client state container buckets */
#define DEFAULT_STATE_BUCKETS 31
@@ -393,7 +394,7 @@ static void sip_outbound_publish_synchronize(struct ast_sip_event_publisher_hand
/* If the publisher client has been started but it is going away stop it */
removed->stop_publishing(state->client);
state->client->started = 0;
- if (ast_sip_push_task(NULL, cancel_refresh_timer_task, ao2_bump(state->client))) {
+ if (ast_sip_push_task(state->client->serializer, 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(state->client, -1);
@@ -614,7 +615,7 @@ fatal:
ast_free(message);
service:
- if (ast_sip_push_task(NULL, sip_publish_client_service_queue, ao2_bump(client))) {
+ if (ast_sip_push_task(client->serializer, sip_publish_client_service_queue, ao2_bump(client))) {
ao2_ref(client, -1);
}
return -1;
@@ -656,7 +657,7 @@ int ast_sip_publish_client_send(struct ast_sip_outbound_publish_client *client,
AST_LIST_INSERT_TAIL(&client->queue, message, entry);
- res = ast_sip_push_task(NULL, sip_publish_client_service_queue, ao2_bump(client));
+ res = ast_sip_push_task(client->serializer, sip_publish_client_service_queue, ao2_bump(client));
if (res) {
ao2_ref(client, -1);
}
@@ -679,16 +680,7 @@ static void sip_outbound_publish_client_destroy(void *obj)
ao2_cleanup(client->datastores);
ao2_cleanup(client->publish);
ast_free(client->transport_name);
-
- /* 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);
- }
+ ast_taskprocessor_unreference(client->serializer);
}
static int explicit_publish_destroy(void *data)
@@ -718,7 +710,7 @@ static int cancel_and_unpublish(struct ast_sip_outbound_publish_client *client)
/* 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);
+ ast_sip_push_task(client->serializer, explicit_publish_destroy, client);
return 0;
}
@@ -728,7 +720,7 @@ static int cancel_and_unpublish(struct ast_sip_outbound_publish_client *client)
}
client->started = 0;
- if (ast_sip_push_task(NULL, cancel_refresh_timer_task, ao2_bump(client))) {
+ if (ast_sip_push_task(client->serializer, 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);
@@ -736,7 +728,7 @@ static int cancel_and_unpublish(struct ast_sip_outbound_publish_client *client)
/* 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))) {
+ if (ast_sip_push_task(client->serializer, 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);
@@ -897,7 +889,7 @@ static void sip_outbound_publish_callback(struct pjsip_publishc_cbparam *param)
if (client->sending) {
client->sending = NULL;
- if (!ast_sip_push_task(NULL, send_unpublish_task, ao2_bump(client))) {
+ if (!ast_sip_push_task(client->serializer, send_unpublish_task, ao2_bump(client))) {
return;
}
ast_log(LOG_WARNING, "Could not send unpublish message on outbound publish '%s'\n",
@@ -981,7 +973,7 @@ end:
ast_free(message);
}
} else {
- if (ast_sip_push_task(NULL, sip_publish_client_service_queue, ao2_bump(client))) {
+ if (ast_sip_push_task(client->serializer, sip_publish_client_service_queue, ao2_bump(client))) {
ao2_ref(client, -1);
}
}
@@ -1053,6 +1045,7 @@ static struct ast_sip_outbound_publish_state *sip_outbound_publish_state_alloc(
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);
+ char tps_name[AST_TASKPROCESSOR_MAX_NAME + 1];
if (!state) {
return NULL;
@@ -1070,6 +1063,17 @@ static struct ast_sip_outbound_publish_state *sip_outbound_publish_state_alloc(
return NULL;
}
+ /* Create name with seq number appended. */
+ ast_taskprocessor_build_name(tps_name, sizeof(tps_name), "pjsip/outpub/%s",
+ ast_sorcery_object_get_id(publish));
+
+ state->client->serializer = ast_sip_create_serializer_group_named(tps_name,
+ shutdown_group);
+ if (!state->client->serializer) {
+ ao2_ref(state, -1);
+ return NULL;
+ }
+
state->client->timer.user_data = state->client;
state->client->timer.cb = sip_outbound_publish_timer_cb;
state->client->transport_name = ast_strdup(publish->transport);
@@ -1082,7 +1086,7 @@ static struct ast_sip_outbound_publish_state *sip_outbound_publish_state_alloc(
static int initialize_publish_client(struct ast_sip_outbound_publish *publish,
struct ast_sip_outbound_publish_state *state)
{
- if (ast_sip_push_task_synchronous(NULL, sip_outbound_publish_client_alloc, state->client)) {
+ if (ast_sip_push_task_synchronous(state->client->serializer, 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(publish));
return -1;
@@ -1219,16 +1223,48 @@ static int outbound_auth_handler(const struct aco_option *opt, struct ast_variab
return ast_sip_auth_vector_init(&publish->outbound_auths, var->value);
}
+
+static int unload_module(void)
+{
+ int remaining;
+
+ ast_sorcery_object_unregister(ast_sip_get_sorcery(), "outbound-publish");
+
+ ao2_global_obj_release(current_states);
+
+ /* Wait for publication serializers to get destroyed. */
+ ast_debug(2, "Waiting for publication to complete for unload.\n");
+ remaining = ast_serializer_shutdown_group_join(shutdown_group, MAX_UNLOAD_TIMEOUT_TIME);
+ if (remaining) {
+ ast_log(LOG_WARNING, "Unload incomplete. Could not stop %d outbound publications. 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)
{
CHECK_PJSIP_MODULE_LOADED();
+ shutdown_group = ast_serializer_shutdown_group_alloc();
+ if (!shutdown_group) {
+ return AST_MODULE_LOAD_FAILURE;
+ }
+
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,
sip_outbound_publish_apply)) {
ast_log(LOG_ERROR, "Unable to register 'outbound-publish' type with sorcery\n");
+ unload_module();
return AST_MODULE_LOAD_DECLINE;
}
@@ -1264,49 +1300,6 @@ static int reload_module(void)
return 0;
}
-static int unload_module(void)
-{
- 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) {
- 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");
- ast_sorcery_object_unregister(ast_sip_get_sorcery(), "outbound-publish");
- }
-
- return res;
-}
-
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "PJSIP Outbound Publish Support",
.load = load_module,
.reload = reload_module,
diff --git a/res/res_pjsip_publish_asterisk.c b/res/res_pjsip_publish_asterisk.c
index 002d976d4..b32408aa3 100644
--- a/res/res_pjsip_publish_asterisk.c
+++ b/res/res_pjsip_publish_asterisk.c
@@ -858,6 +858,11 @@ static int load_module(void)
{
CHECK_PJSIP_PUBSUB_MODULE_LOADED();
+ if (ast_eid_is_empty(&ast_eid_default)) {
+ ast_log(LOG_ERROR, "Entity ID is not set.\n");
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
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");
diff --git a/res/res_pjsip_pubsub.c b/res/res_pjsip_pubsub.c
index 79019fb25..3ac3f342f 100644
--- a/res/res_pjsip_pubsub.c
+++ b/res/res_pjsip_pubsub.c
@@ -378,6 +378,20 @@ struct subscription_persistence {
};
/*!
+ * \brief The state of the subscription tree
+ */
+enum sip_subscription_tree_state {
+ /*! Normal operation */
+ SIP_SUB_TREE_NORMAL = 0,
+ /*! A terminate has been requested by Asterisk, the client, or pjproject */
+ SIP_SUB_TREE_TERMINATE_PENDING,
+ /*! The terminate is in progress */
+ SIP_SUB_TREE_TERMINATE_IN_PROGRESS,
+ /*! The terminate process has finished and the subscription tree is no longer valid */
+ SIP_SUB_TREE_TERMINATED,
+};
+
+/*!
* \brief A tree of SIP subscriptions
*
* Because of the ability to subscribe to resource lists, a SIP
@@ -411,8 +425,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;
+ /*! Subscription tree state */
+ enum sip_subscription_tree_state state;
};
/*!
@@ -879,15 +893,15 @@ static void build_node_children(struct ast_sip_endpoint *endpoint, const struct
"allocation error afterwards\n", resource);
continue;
}
- ast_debug(1, "Subscription to leaf resource %s resulted in success. Adding to parent %s\n",
+ ast_debug(2, "Subscription to leaf resource %s resulted in success. Adding to parent %s\n",
resource, parent->resource);
AST_VECTOR_APPEND(&parent->children, current);
} else {
- ast_debug(1, "Subscription to leaf resource %s resulted in error response %d\n",
+ ast_debug(2, "Subscription to leaf resource %s resulted in error response %d\n",
resource, resp);
}
} else {
- ast_debug(1, "Resource %s (child of %s) is a list\n", resource, parent->resource);
+ ast_debug(2, "Resource %s (child of %s) is a list\n", resource, parent->resource);
current = tree_node_alloc(resource, visited, child_list->full_state);
if (!current) {
ast_debug(1, "Cannot build children of resource %s due to allocation failure\n", resource);
@@ -898,7 +912,7 @@ static void build_node_children(struct ast_sip_endpoint *endpoint, const struct
ast_debug(1, "List %s had no successful children.\n", resource);
AST_VECTOR_APPEND(&parent->children, current);
} else {
- ast_debug(1, "List %s had successful children. Adding to parent %s\n",
+ ast_debug(2, "List %s had successful children. Adding to parent %s\n",
resource, parent->resource);
tree_node_destroy(current);
}
@@ -970,7 +984,7 @@ static int build_resource_tree(struct ast_sip_endpoint *endpoint, const struct a
struct resources visited;
if (!has_eventlist_support || !(list = retrieve_resource_list(resource, handler->event_name))) {
- ast_debug(1, "Subscription to resource %s is not to a list\n", resource);
+ ast_debug(2, "Subscription to resource %s is not to a list\n", resource);
tree->root = tree_node_alloc(resource, NULL, 0);
if (!tree->root) {
return 500;
@@ -978,7 +992,7 @@ static int build_resource_tree(struct ast_sip_endpoint *endpoint, const struct a
return handler->notifier->new_subscribe(endpoint, resource);
}
- ast_debug(1, "Subscription to resource %s is a list\n", resource);
+ ast_debug(2, "Subscription to resource %s is a list\n", resource);
if (AST_VECTOR_INIT(&visited, AST_VECTOR_SIZE(&list->items))) {
return 500;
}
@@ -1037,7 +1051,7 @@ static void remove_subscription(struct sip_subscription_tree *obj)
if (i == obj) {
AST_RWLIST_REMOVE_CURRENT(next);
if (i->root) {
- ast_debug(1, "Removing subscription to resource %s from list of subscriptions\n",
+ ast_debug(2, "Removing subscription to resource %s from list of subscriptions\n",
ast_sip_subscription_get_resource_name(i->root));
}
break;
@@ -1329,6 +1343,10 @@ static struct sip_subscription_tree *create_subscription_tree(const struct ast_s
pjsip_evsub_create_uas(dlg, &pubsub_cb, rdata, 0, &sub_tree->evsub);
subscription_setup_dialog(sub_tree, dlg);
+#ifdef HAVE_PJSIP_EVSUB_GRP_LOCK
+ pjsip_evsub_add_ref(sub_tree->evsub);
+#endif
+
ast_sip_mod_data_set(dlg->pool, dlg->mod_data, pubsub_module.id, MOD_DATA_MSG,
pjsip_msg_clone(dlg->pool, rdata->msg_info.msg));
@@ -2252,10 +2270,8 @@ 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)) {
+ /* do not call pjsip_tx_data_dec_ref(tdata). The pjsip_dlg_send_request deletes the message on error */
return -1;
}
@@ -2270,21 +2286,32 @@ static int serialized_send_notify(void *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
+ * and now a new SUBSCRIBE arrived requiring full state to be
+ * sent out in an immediate NOTIFY. It's also possible that we're
+ * already processing a terminate. If that has happened, we need to
* bail out here instead of sending the batched NOTIFY.
*/
- if (!sub_tree->send_scheduled_notify) {
+
+ if (sub_tree->state >= SIP_SUB_TREE_TERMINATE_IN_PROGRESS
+ || !sub_tree->send_scheduled_notify) {
pjsip_dlg_dec_lock(dlg);
ao2_cleanup(sub_tree);
return 0;
}
+ if (sub_tree->root->subscription_state == PJSIP_EVSUB_STATE_TERMINATED) {
+ sub_tree->state = SIP_SUB_TREE_TERMINATE_IN_PROGRESS;
+ }
+
send_notify(sub_tree, 0);
- ast_test_suite_event_notify("SUBSCRIPTION_STATE_CHANGED",
- "Resource: %s",
- sub_tree->root->resource);
+
+ ast_test_suite_event_notify(
+ sub_tree->state == SIP_SUB_TREE_TERMINATED
+ ? "SUBSCRIPTION_TERMINATED" : "SUBSCRIPTION_STATE_CHANGED",
+ "Resource: %s", sub_tree->root->resource);
+
sub_tree->notify_sched_id = -1;
pjsip_dlg_dec_lock(dlg);
ao2_cleanup(sub_tree);
@@ -2296,7 +2323,10 @@ static int sched_cb(const void *data)
struct sip_subscription_tree *sub_tree = (struct sip_subscription_tree *) data;
/* We don't need to bump the refcount of sub_tree since we bumped it when scheduling this task */
- ast_sip_push_task(sub_tree->serializer, serialized_send_notify, sub_tree);
+ if (ast_sip_push_task(sub_tree->serializer, serialized_send_notify, sub_tree)) {
+ ao2_cleanup(sub_tree);
+ }
+
return 0;
}
@@ -2307,12 +2337,13 @@ static int schedule_notification(struct sip_subscription_tree *sub_tree)
return 0;
}
+ sub_tree->send_scheduled_notify = 1;
sub_tree->notify_sched_id = ast_sched_add(sched, sub_tree->notification_batch_interval, sched_cb, ao2_bump(sub_tree));
if (sub_tree->notify_sched_id < 0) {
+ ao2_cleanup(sub_tree);
return -1;
}
- sub_tree->send_scheduled_notify = 1;
return 0;
}
@@ -2324,7 +2355,7 @@ int ast_sip_subscription_notify(struct ast_sip_subscription *sub, struct ast_sip
pjsip_dlg_inc_lock(dlg);
- if (!sub->tree->evsub) {
+ if (sub->tree->state != SIP_SUB_TREE_NORMAL) {
pjsip_dlg_dec_lock(dlg);
return 0;
}
@@ -2338,6 +2369,7 @@ int ast_sip_subscription_notify(struct ast_sip_subscription *sub, struct ast_sip
sub->body_changed = 1;
if (terminate) {
sub->subscription_state = PJSIP_EVSUB_STATE_TERMINATED;
+ sub->tree->state = SIP_SUB_TREE_TERMINATE_PENDING;
}
if (sub->tree->notification_batch_interval) {
@@ -2345,6 +2377,9 @@ int ast_sip_subscription_notify(struct ast_sip_subscription *sub, struct ast_sip
} else {
/* See the note in pubsub_on_rx_refresh() for why sub->tree is refbumped here */
ao2_ref(sub->tree, +1);
+ if (terminate) {
+ sub->tree->state = SIP_SUB_TREE_TERMINATE_IN_PROGRESS;
+ }
res = send_notify(sub->tree, 0);
ast_test_suite_event_notify(terminate ? "SUBSCRIPTION_TERMINATED" : "SUBSCRIPTION_STATE_CHANGED",
"Resource: %s",
@@ -2995,11 +3030,14 @@ static struct ast_sip_publication *publish_request_initial(struct ast_sip_endpoi
resource = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "inbound-publication", resource_name);
if (!resource) {
+ ast_debug(1, "No 'inbound-publication' defined for resource '%s'\n", resource_name);
pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 404, NULL, NULL, NULL);
return NULL;
}
if (!ast_strlen_zero(resource->endpoint) && strcmp(resource->endpoint, ast_sorcery_object_get_id(endpoint))) {
+ ast_debug(1, "Resource %s has a defined endpoint '%s', but does not match endpoint '%s' that received the request\n",
+ resource_name, resource->endpoint, ast_sorcery_object_get_id(endpoint));
pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
return NULL;
}
@@ -3011,6 +3049,7 @@ static struct ast_sip_publication *publish_request_initial(struct ast_sip_endpoi
}
if (!event_configuration_name) {
+ ast_debug(1, "Event '%s' is not configured for '%s'\n", handler->event_name, resource_name);
pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 404, NULL, NULL, NULL);
return NULL;
}
@@ -3328,71 +3367,72 @@ static void set_state_terminated(struct ast_sip_subscription *sub)
}
}
-/* 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.
+ * \brief Callback sequence for subscription terminate:
*
- * 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.
+ * * Client initiated:
+ * pjproject receives SUBSCRIBE on the subscription's serializer thread
+ * calls pubsub_on_rx_refresh with dialog locked
+ * pubsub_on_rx_refresh sets TERMINATE_PENDING
+ * pushes serialized_pubsub_on_refresh_timeout
+ * returns to pjproject
+ * pjproject calls pubsub_on_evsub_state
+ * pubsub_evsub_set_state checks state == TERMINATE_IN_PROGRESS (no)
+ * ignore and return
+ * pjproject unlocks dialog
+ * serialized_pubsub_on_refresh_timeout starts (1)
+ * locks dialog
+ * checks state == TERMINATE_PENDING
+ * sets TERMINATE_IN_PROGRESS
+ * calls send_notify (2)
+ * send_notify ultimately calls pjsip_evsub_send_request
+ * pjsip_evsub_send_request calls evsub's set_state
+ * set_state calls pubsub_evsub_set_state
+ * pubsub_evsub_set_state checks state == TERMINATE_IN_PROGRESS
+ * removes the subscriptions
+ * cleans up references to evsub
+ * sets state = TERMINATED
+ * serialized_pubsub_on_refresh_timeout unlocks dialog
*
- * 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".
+ * * Subscription timer expires:
+ * pjproject timer expires
+ * locks dialog
+ * calls pubsub_on_server_timeout
+ * pubsub_on_server_timeout checks state == NORMAL
+ * sets TERMINATE_PENDING
+ * pushes serialized_pubsub_on_refresh_timeout
+ * returns to pjproject
+ * pjproject unlocks dialog
+ * serialized_pubsub_on_refresh_timeout starts
+ * See (1) Above
*
- * 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.
+ * * ast_sip_subscription_notify is called
+ * checks state == NORMAL
+ * if not batched...
+ * sets TERMINATE_IN_PROGRESS (if terminate is requested)
+ * calls send_notify
+ * See (2) Above
+ * if batched...
+ * sets TERMINATE_PENDING
+ * schedules task
+ * scheduler runs sched_task
+ * sched_task pushes serialized_send_notify
+ * serialized_send_notify starts
+ * checks state <= TERMINATE_PENDING
+ * if state == TERMINATE_PENDING set state = TERMINATE_IN_PROGRESS
+ * call send_notify
+ * See (2) Above
*
- * 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
+ */
+
+/*!
+ * \brief PJSIP callback when underlying SIP subscription changes state
*
- * \li The subscription tree's last_notify field
- * \li The subscription tree's evsub pointer
+ * Although this function is called for every state change, we only care
+ * about the TERMINATED state, and only when we're actually processing the final
+ * notify (SIP_SUB_TREE_TERMINATE_IN_PROGRESS). In this case, we do all
+ * the subscription tree cleanup tasks and decrement the evsub reference.
*/
static void pubsub_on_evsub_state(pjsip_evsub *evsub, pjsip_event *event)
{
@@ -3405,51 +3445,55 @@ static void pubsub_on_evsub_state(pjsip_evsub *evsub, pjsip_event *event)
}
sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
- if (!sub_tree) {
+ if (!sub_tree || sub_tree->state != SIP_SUB_TREE_TERMINATE_IN_PROGRESS) {
+ ast_debug(1, "Possible terminate race prevented %p\n", sub_tree);
return;
}
- 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;
- }
- }
-
remove_subscription(sub_tree);
+
pjsip_evsub_set_mod_data(evsub, pubsub_module.id, NULL);
+
+#ifdef HAVE_PJSIP_EVSUB_GRP_LOCK
+ pjsip_evsub_dec_ref(sub_tree->evsub);
+#endif
+
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);
+ sub_tree->state = SIP_SUB_TREE_TERMINATED;
/* Remove evsub's reference to the sub_tree */
ao2_ref(sub_tree, -1);
}
-static int serialized_pubsub_on_rx_refresh(void *userdata)
+static int serialized_pubsub_on_refresh_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) {
+ if (sub_tree->state >= SIP_SUB_TREE_TERMINATE_IN_PROGRESS) {
+ ast_debug(1, "Possible terminate race prevented %p %d\n", sub_tree->evsub, sub_tree->state);
pjsip_dlg_dec_lock(dlg);
+ ao2_cleanup(sub_tree);
return 0;
}
- if (pjsip_evsub_get_state(sub_tree->evsub) == PJSIP_EVSUB_STATE_TERMINATED) {
+ if (sub_tree->state == SIP_SUB_TREE_TERMINATE_PENDING) {
+ sub_tree->state = SIP_SUB_TREE_TERMINATE_IN_PROGRESS;
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);
+ "SUBSCRIPTION_TERMINATED" : "SUBSCRIPTION_REFRESHED",
+ "Resource: %s", sub_tree->root->resource);
pjsip_dlg_dec_lock(dlg);
ao2_cleanup(sub_tree);
@@ -3462,10 +3506,8 @@ static int serialized_pubsub_on_rx_refresh(void *userdata)
* 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.
+ * In either case we push serialized_pubsub_on_refresh_timeout to send an
+ * appropriate NOTIFY request.
*/
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)
@@ -3473,18 +3515,24 @@ static void pubsub_on_rx_refresh(pjsip_evsub *evsub, pjsip_rx_data *rdata,
struct sip_subscription_tree *sub_tree;
sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
- if (!sub_tree) {
+ if (!sub_tree || sub_tree->state != SIP_SUB_TREE_NORMAL) {
+ ast_debug(1, "Possible terminate race prevented %p %d\n", sub_tree, sub_tree ? sub_tree->state : -1 );
return;
}
/* PJSIP will set the evsub's state to terminated before calling into this function
* if the Expires value of the incoming SUBSCRIBE is 0.
*/
- 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);
- }
+
+ if (pjsip_evsub_get_state(sub_tree->evsub) == PJSIP_EVSUB_STATE_TERMINATED) {
+ sub_tree->state = SIP_SUB_TREE_TERMINATE_PENDING;
+ }
+
+ if (ast_sip_push_task(sub_tree->serializer, serialized_pubsub_on_refresh_timeout, ao2_bump(sub_tree))) {
+ /* If we can't push the NOTIFY refreshing task...we'll just go with it. */
+ ast_log(LOG_ERROR, "Failed to push task to send NOTIFY.\n");
+ sub_tree->state = SIP_SUB_TREE_NORMAL;
+ ao2_ref(sub_tree, -1);
}
if (sub_tree->is_list) {
@@ -3495,9 +3543,9 @@ static void pubsub_on_rx_refresh(pjsip_evsub *evsub, pjsip_rx_data *rdata,
static void pubsub_on_rx_notify(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)
{
- struct ast_sip_subscription *sub = pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
+ struct ast_sip_subscription *sub;
- if (!sub) {
+ if (!(sub = pjsip_evsub_get_mod_data(evsub, pubsub_module.id))) {
return;
}
@@ -3510,45 +3558,62 @@ static int serialized_pubsub_on_client_refresh(void *userdata)
struct sip_subscription_tree *sub_tree = userdata;
pjsip_tx_data *tdata;
+ if (!sub_tree->evsub) {
+ ao2_cleanup(sub_tree);
+ return 0;
+ }
+
if (pjsip_evsub_initiate(sub_tree->evsub, NULL, -1, &tdata) == PJ_SUCCESS) {
pjsip_evsub_send_request(sub_tree->evsub, tdata);
} else {
pjsip_evsub_terminate(sub_tree->evsub, PJ_TRUE);
}
+
ao2_cleanup(sub_tree);
return 0;
}
static void pubsub_on_client_refresh(pjsip_evsub *evsub)
{
- struct sip_subscription_tree *sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
+ struct sip_subscription_tree *sub_tree;
+
+ if (!(sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id))) {
+ return;
+ }
- ao2_ref(sub_tree, +1);
- ast_sip_push_task(sub_tree->serializer, serialized_pubsub_on_client_refresh, sub_tree);
+ if (ast_sip_push_task(sub_tree->serializer, serialized_pubsub_on_client_refresh, ao2_bump(sub_tree))) {
+ ao2_cleanup(sub_tree);
+ }
}
static void pubsub_on_server_timeout(pjsip_evsub *evsub)
{
+ struct sip_subscription_tree *sub_tree;
- struct sip_subscription_tree *sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
- if (!sub_tree) {
- /* 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.
- */
+ /* 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.
+ */
+
+ sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
+ if (!sub_tree || sub_tree->state != SIP_SUB_TREE_NORMAL) {
+ ast_debug(1, "Possible terminate race prevented %p %d\n", sub_tree, sub_tree ? sub_tree->state : -1 );
return;
}
- ao2_ref(sub_tree, +1);
- ast_sip_push_task(sub_tree->serializer, serialized_pubsub_on_server_timeout, sub_tree);
+ sub_tree->state = SIP_SUB_TREE_TERMINATE_PENDING;
+ if (ast_sip_push_task(sub_tree->serializer, serialized_pubsub_on_refresh_timeout, ao2_bump(sub_tree))) {
+ sub_tree->state = SIP_SUB_TREE_NORMAL;
+ ao2_cleanup(sub_tree);
+ }
}
static int ami_subscription_detail(struct sip_subscription_tree *sub_tree,
diff --git a/res/res_pjsip_refer.c b/res/res_pjsip_refer.c
index f3af65c3c..e5bb90e5c 100644
--- a/res/res_pjsip_refer.c
+++ b/res/res_pjsip_refer.c
@@ -969,7 +969,8 @@ inv_replace_failed:
session->defer_terminate = 1;
ast_hangup(session->channel);
- if (pjsip_inv_end_session(session->inv_session, response, NULL, &packet) == PJ_SUCCESS) {
+ if (pjsip_inv_end_session(session->inv_session, response, NULL, &packet) == PJ_SUCCESS
+ && packet) {
ast_sip_session_send_response(session, packet);
}
} else {
diff --git a/res/res_pjsip_registrar.c b/res/res_pjsip_registrar.c
index aef0e164b..53209575f 100644
--- a/res/res_pjsip_registrar.c
+++ b/res/res_pjsip_registrar.c
@@ -525,12 +525,12 @@ static int register_aor(pjsip_rx_data *rdata,
struct ao2_container *contacts = NULL;
struct ast_named_lock *lock;
- lock = ast_named_lock_get(AST_NAMED_LOCK_TYPE_RWLOCK, "aor", aor_name);
+ lock = ast_named_lock_get(AST_NAMED_LOCK_TYPE_MUTEX, "aor", aor_name);
if (!lock) {
return PJ_TRUE;
}
- ao2_wrlock(lock);
+ ao2_lock(lock);
contacts = ast_sip_location_retrieve_aor_contacts_nolock(aor);
if (!contacts) {
ao2_unlock(lock);
diff --git a/res/res_pjsip_registrar_expire.c b/res/res_pjsip_registrar_expire.c
index 0fddbda91..aeefc3fbc 100644
--- a/res/res_pjsip_registrar_expire.c
+++ b/res/res_pjsip_registrar_expire.c
@@ -44,7 +44,7 @@ static int expire_contact(void *obj, void *arg, int flags)
struct ast_sip_contact *contact = obj;
struct ast_named_lock *lock;
- lock = ast_named_lock_get(AST_NAMED_LOCK_TYPE_RWLOCK, "aor", contact->aor);
+ lock = ast_named_lock_get(AST_NAMED_LOCK_TYPE_MUTEX, "aor", contact->aor);
if (!lock) {
return 0;
}
@@ -53,7 +53,7 @@ static int expire_contact(void *obj, void *arg, int flags)
* We need to check the expiration again with the aor lock held
* in case another thread is attempting to renew the contact.
*/
- ao2_wrlock(lock);
+ ao2_lock(lock);
if (ast_tvdiff_ms(ast_tvnow(), contact->expiration_time) > 0) {
ast_sip_location_delete_contact(contact);
}
diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c
index 18a7f3f6a..029eb5d27 100644
--- a/res/res_pjsip_sdp_rtp.c
+++ b/res/res_pjsip_sdp_rtp.c
@@ -420,7 +420,7 @@ static pjmedia_sdp_attr* generate_fmtp_attr(pj_pool_t *pool, struct ast_format *
*++tmp = '\0';
/* ast...generate gives us everything, just need value */
tmp = strchr(ast_str_buffer(fmtp0), ':');
- if (tmp && tmp + 1) {
+ if (tmp && tmp[1] != '\0') {
fmtp1 = pj_str(tmp + 1);
} else {
fmtp1 = pj_str(ast_str_buffer(fmtp0));
diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c
index 18bb6abe8..a41e0aa5d 100644
--- a/res/res_pjsip_session.c
+++ b/res/res_pjsip_session.c
@@ -46,6 +46,7 @@
#include "asterisk/acl.h"
#include "asterisk/features_config.h"
#include "asterisk/pickup.h"
+#include "asterisk/test.h"
#define SDP_HANDLER_BUCKETS 11
@@ -53,10 +54,10 @@
#define MOD_DATA_NAT_HOOK "nat_hook"
/* Some forward declarations */
-static void handle_incoming_request(struct ast_sip_session *session, pjsip_rx_data *rdata, pjsip_event_id_e type);
-static void handle_incoming_response(struct ast_sip_session *session, pjsip_rx_data *rdata, pjsip_event_id_e type,
+static void handle_incoming_request(struct ast_sip_session *session, pjsip_rx_data *rdata);
+static void handle_incoming_response(struct ast_sip_session *session, pjsip_rx_data *rdata,
enum ast_sip_session_response_priority response_priority);
-static int handle_incoming(struct ast_sip_session *session, pjsip_rx_data *rdata, pjsip_event_id_e type,
+static int handle_incoming(struct ast_sip_session *session, pjsip_rx_data *rdata,
enum ast_sip_session_response_priority response_priority);
static void handle_outgoing_request(struct ast_sip_session *session, pjsip_tx_data *tdata);
static void handle_outgoing_response(struct ast_sip_session *session, pjsip_tx_data *tdata);
@@ -791,12 +792,14 @@ static int delay_request(struct ast_sip_session *session,
static pjmedia_sdp_session *generate_session_refresh_sdp(struct ast_sip_session *session)
{
pjsip_inv_session *inv_session = session->inv_session;
- const pjmedia_sdp_session *previous_sdp;
+ const pjmedia_sdp_session *previous_sdp = NULL;
- if (pjmedia_sdp_neg_was_answer_remote(inv_session->neg)) {
- pjmedia_sdp_neg_get_active_remote(inv_session->neg, &previous_sdp);
- } else {
- pjmedia_sdp_neg_get_active_local(inv_session->neg, &previous_sdp);
+ if (inv_session->neg) {
+ if (pjmedia_sdp_neg_was_answer_remote(inv_session->neg)) {
+ pjmedia_sdp_neg_get_active_remote(inv_session->neg, &previous_sdp);
+ } else {
+ pjmedia_sdp_neg_get_active_local(inv_session->neg, &previous_sdp);
+ }
}
return create_local_sdp(inv_session, session, previous_sdp);
}
@@ -916,7 +919,9 @@ 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) {
+ if (inv_session->neg
+ && pjmedia_sdp_neg_get_state(inv_session->neg)
+ != PJMEDIA_SDP_NEG_STATE_DONE) {
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,
@@ -1554,6 +1559,11 @@ void ast_sip_session_suspend(struct ast_sip_session *session)
return;
}
+ if (ast_taskprocessor_is_suspended(session->serializer)) {
+ /* The serializer already suspended. */
+ return;
+ }
+
suspender = ao2_alloc(sizeof(*suspender), sip_session_suspender_dtor);
if (!suspender) {
/* We will just have to hope that the system does not deadlock */
@@ -1578,6 +1588,8 @@ void ast_sip_session_suspend(struct ast_sip_session *session)
ast_cond_wait(&suspender->cond_suspended, ao2_object_get_lockaddr(suspender));
}
ao2_unlock(suspender);
+
+ ast_taskprocessor_suspend(session->serializer);
}
void ast_sip_session_unsuspend(struct ast_sip_session *session)
@@ -1597,6 +1609,8 @@ void ast_sip_session_unsuspend(struct ast_sip_session *session)
ao2_unlock(suspender);
ao2_ref(suspender, -1);
+
+ ast_taskprocessor_unsuspend(session->serializer);
}
/*!
@@ -2143,7 +2157,7 @@ static int new_invite(void *data)
}
ast_sip_session_send_response(invite->session, tdata);
- handle_incoming_request(invite->session, invite->rdata, PJSIP_EVENT_RX_MSG);
+ handle_incoming_request(invite->session, invite->rdata);
return 0;
}
@@ -2315,7 +2329,8 @@ static void reschedule_reinvite(struct ast_sip_session *session, ast_sip_session
static void __print_debug_details(const char *function, pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e)
{
- struct ast_sip_session *session;
+ int id = session_module.id;
+ struct ast_sip_session *session = NULL;
if (!DEBUG_ATLEAST(5)) {
/* Debug not spamy enough */
@@ -2330,7 +2345,9 @@ static void __print_debug_details(const char *function, pjsip_inv_session *inv,
pjsip_tsx_state_str(tsx->state));
return;
}
- session = inv->mod_data[session_module.id];
+ if (id > -1) {
+ session = inv->mod_data[session_module.id];
+ }
if (!session) {
ast_log(LOG_DEBUG, "inv_session %p has no ast session\n", inv);
} else {
@@ -2361,7 +2378,7 @@ static void __print_debug_details(const char *function, pjsip_inv_session *inv,
#define print_debug_details(inv, tsx, e) __print_debug_details(__PRETTY_FUNCTION__, (inv), (tsx), (e))
-static void handle_incoming_request(struct ast_sip_session *session, pjsip_rx_data *rdata, pjsip_event_id_e type)
+static void handle_incoming_request(struct ast_sip_session *session, pjsip_rx_data *rdata)
{
struct ast_sip_session_supplement *supplement;
struct pjsip_request_line req = rdata->msg_info.msg->line.req;
@@ -2376,7 +2393,7 @@ static void handle_incoming_request(struct ast_sip_session *session, pjsip_rx_da
}
}
-static void handle_incoming_response(struct ast_sip_session *session, pjsip_rx_data *rdata, pjsip_event_id_e type,
+static void handle_incoming_response(struct ast_sip_session *session, pjsip_rx_data *rdata,
enum ast_sip_session_response_priority response_priority)
{
struct ast_sip_session_supplement *supplement;
@@ -2395,16 +2412,16 @@ static void handle_incoming_response(struct ast_sip_session *session, pjsip_rx_d
}
}
-static int handle_incoming(struct ast_sip_session *session, pjsip_rx_data *rdata, pjsip_event_id_e type,
+static int handle_incoming(struct ast_sip_session *session, pjsip_rx_data *rdata,
enum ast_sip_session_response_priority response_priority)
{
ast_debug(3, "Received %s\n", rdata->msg_info.msg->type == PJSIP_REQUEST_MSG ?
"request" : "response");
if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) {
- handle_incoming_request(session, rdata, type);
+ handle_incoming_request(session, rdata);
} else {
- handle_incoming_response(session, rdata, type, response_priority);
+ handle_incoming_response(session, rdata, response_priority);
}
return 0;
@@ -2487,6 +2504,36 @@ static int session_end_completion(void *vsession)
return 0;
}
+static void handle_incoming_before_media(pjsip_inv_session *inv,
+ struct ast_sip_session *session, pjsip_rx_data *rdata)
+{
+ pjsip_msg *msg;
+
+ handle_incoming(session, rdata, AST_SIP_SESSION_BEFORE_MEDIA);
+ msg = rdata->msg_info.msg;
+ if (msg->type == PJSIP_REQUEST_MSG
+ && msg->line.req.method.id == PJSIP_ACK_METHOD
+ && pjmedia_sdp_neg_get_state(inv->neg) != PJMEDIA_SDP_NEG_STATE_DONE) {
+ pjsip_tx_data *tdata;
+
+ /*
+ * SDP negotiation failed on an incoming call that delayed
+ * negotiation and then gave us an invalid SDP answer. We
+ * need to send a BYE to end the call because of the invalid
+ * SDP answer.
+ */
+ ast_debug(1,
+ "Endpoint '%s(%s)': Ending session due to incomplete SDP negotiation. %s\n",
+ ast_sorcery_object_get_id(session->endpoint),
+ session->channel ? ast_channel_name(session->channel) : "",
+ pjsip_rx_data_get_info(rdata));
+ if (pjsip_inv_end_session(inv, 400, NULL, &tdata) == PJ_SUCCESS
+ && tdata) {
+ ast_sip_session_send_request(session, tdata);
+ }
+ }
+}
+
static void session_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e)
{
struct ast_sip_session *session = inv->mod_data[session_module.id];
@@ -2508,8 +2555,7 @@ static void session_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e)
handle_outgoing(session, e->body.tx_msg.tdata);
break;
case PJSIP_EVENT_RX_MSG:
- handle_incoming(session, e->body.rx_msg.rdata, type,
- AST_SIP_SESSION_BEFORE_MEDIA);
+ handle_incoming_before_media(inv, session, e->body.rx_msg.rdata);
break;
case PJSIP_EVENT_TSX_STATE:
ast_debug(3, "Source of transaction state change is %s\n", pjsip_event_str(e->body.tsx_state.type));
@@ -2519,8 +2565,7 @@ static void session_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e)
handle_outgoing(session, e->body.tsx_state.src.tdata);
break;
case PJSIP_EVENT_RX_MSG:
- handle_incoming(session, e->body.tsx_state.src.rdata, type,
- AST_SIP_SESSION_BEFORE_MEDIA);
+ handle_incoming_before_media(inv, session, e->body.tsx_state.src.rdata);
break;
case PJSIP_EVENT_TRANSPORT_ERROR:
case PJSIP_EVENT_TIMER:
@@ -2552,9 +2597,22 @@ static void session_inv_on_new_session(pjsip_inv_session *inv, pjsip_event *e)
static void session_inv_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e)
{
ast_sip_session_response_cb cb;
- struct ast_sip_session *session = inv->mod_data[session_module.id];
+ int id = session_module.id;
+ struct ast_sip_session *session;
pjsip_tx_data *tdata;
+ /*
+ * A race condition exists at shutdown where the res_pjsip_session can be
+ * unloaded but this callback may still get called afterwards. In this case
+ * the id may end up being -1 which is useless to us. To work around this
+ * we store the current value and check/use it.
+ */
+ if (id < 0) {
+ return;
+ }
+
+ session = inv->mod_data[id];
+
print_debug_details(inv, tsx, e);
if (!session) {
/* The session has ended. Ignore the transaction change. */
@@ -2568,10 +2626,10 @@ static void session_inv_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_trans
* we transfer the data into the transaction. This way, when we receive a response, we
* can dig this data out again
*/
- tsx->mod_data[session_module.id] = e->body.tsx_state.src.tdata->mod_data[session_module.id];
+ tsx->mod_data[id] = e->body.tsx_state.src.tdata->mod_data[id];
break;
case PJSIP_EVENT_RX_MSG:
- cb = ast_sip_mod_data_get(tsx->mod_data, session_module.id, MOD_DATA_ON_RESPONSE);
+ cb = ast_sip_mod_data_get(tsx->mod_data, id, MOD_DATA_ON_RESPONSE);
/* 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
@@ -2579,8 +2637,8 @@ static void session_inv_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_trans
*/
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);
+ handle_incoming(session, e->body.tsx_state.src.rdata,
+ AST_SIP_SESSION_AFTER_MEDIA);
}
if (tsx->method.id == PJSIP_INVITE_METHOD) {
if (tsx->role == PJSIP_ROLE_UAC) {
@@ -2603,20 +2661,57 @@ static void session_inv_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_trans
}
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) {
+ if (pjsip_inv_end_session(inv, 500, NULL, &tdata) == PJ_SUCCESS
+ && tdata) {
ast_sip_session_send_request(session, tdata);
}
}
}
} else if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
if (inv->cancelling && tsx->status_code == PJSIP_SC_OK) {
- /* This is a race condition detailed in RFC 5407 section 3.1.2.
- * We sent a CANCEL at the same time that the UAS sent us a 200 OK for
- * the original INVITE. As a result, we have now received a 200 OK for
- * a cancelled call. Our role is to immediately send a BYE to end the
- * dialog.
+ int sdp_negotiation_done =
+ pjmedia_sdp_neg_get_state(inv->neg) == PJMEDIA_SDP_NEG_STATE_DONE;
+
+ /*
+ * We can get here for the following reasons.
+ *
+ * 1) The race condition detailed in RFC5407 section 3.1.2.
+ * We sent a CANCEL at the same time that the UAS sent us a
+ * 200 OK with a valid SDP for the original INVITE. As a
+ * result, we have now received a 200 OK for a cancelled
+ * call and the SDP negotiation is complete. We need to
+ * immediately send a BYE to end the dialog.
+ *
+ * 2) We sent a CANCEL and hit the race condition but the
+ * UAS sent us an invalid SDP with the 200 OK. In this case
+ * the SDP negotiation is incomplete and PJPROJECT has
+ * already sent the BYE for us because of the invalid SDP.
+ *
+ * 3) We didn't send a CANCEL but the UAS sent us an invalid
+ * SDP with the 200 OK. In this case the SDP negotiation is
+ * incomplete and PJPROJECT has already sent the BYE for us
+ * because of the invalid SDP.
*/
- if (pjsip_inv_end_session(inv, 500, NULL, &tdata) == PJ_SUCCESS) {
+ ast_test_suite_event_notify("PJSIP_SESSION_CANCELED",
+ "Endpoint: %s\r\n"
+ "Channel: %s\r\n"
+ "Message: %s\r\n"
+ "SDP: %s",
+ ast_sorcery_object_get_id(session->endpoint),
+ session->channel ? ast_channel_name(session->channel) : "",
+ pjsip_rx_data_get_info(e->body.tsx_state.src.rdata),
+ sdp_negotiation_done ? "complete" : "incomplete");
+ if (!sdp_negotiation_done) {
+ ast_debug(1, "Endpoint '%s(%s)': Incomplete SDP negotiation cancelled session. %s\n",
+ ast_sorcery_object_get_id(session->endpoint),
+ session->channel ? ast_channel_name(session->channel) : "",
+ pjsip_rx_data_get_info(e->body.tsx_state.src.rdata));
+ } else if (pjsip_inv_end_session(inv, 500, NULL, &tdata) == PJ_SUCCESS
+ && tdata) {
+ ast_debug(1, "Endpoint '%s(%s)': Ending session due to RFC5407 race condition. %s\n",
+ ast_sorcery_object_get_id(session->endpoint),
+ session->channel ? ast_channel_name(session->channel) : "",
+ pjsip_rx_data_get_info(e->body.tsx_state.src.rdata));
ast_sip_session_send_request(session, tdata);
}
}
@@ -2650,7 +2745,7 @@ static void session_inv_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_trans
* 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;
+ inv->mod_data[id] = NULL;
if (inv->state != PJSIP_INV_STATE_DISCONNECTED) {
session_end(session);
@@ -2673,8 +2768,8 @@ static void session_inv_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_trans
* 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;
+ session = inv->mod_data[id];
+ inv->mod_data[id] = NULL;
pjsip_dlg_dec_lock(inv->dlg);
/*
@@ -2903,8 +2998,7 @@ static pjsip_redirect_op session_inv_on_redirected(pjsip_inv_session *inv, const
return PJSIP_REDIRECT_STOP;
}
- handle_incoming(session, e->body.rx_msg.rdata, PJSIP_EVENT_RX_MSG,
- AST_SIP_SESSION_BEFORE_REDIRECTING);
+ handle_incoming(session, e->body.rx_msg.rdata, AST_SIP_SESSION_BEFORE_REDIRECTING);
uri = pjsip_uri_get_uri(target);
diff --git a/res/res_pjsip_t38.c b/res/res_pjsip_t38.c
index 14207d9c0..992902af2 100644
--- a/res/res_pjsip_t38.c
+++ b/res/res_pjsip_t38.c
@@ -361,7 +361,9 @@ static int t38_interpret_parameters(void *obj)
ast_udptl_set_local_max_ifp(session_media->udptl, state->our_parms.max_ifp);
t38_change_state(data->session, session_media, state, T38_ENABLED);
ast_sip_session_resume_reinvite(data->session);
- } else if (data->session->t38state != T38_ENABLED) {
+ } else if ((data->session->t38state != T38_ENABLED) ||
+ ((data->session->t38state == T38_ENABLED) &&
+ (parameters->request_response == AST_T38_REQUEST_NEGOTIATE))) {
if (t38_initialize_session(data->session, session_media)) {
break;
}
diff --git a/res/res_pjsip_transport_management.c b/res/res_pjsip_transport_management.c
index afd94eb1f..1cf8e5046 100644
--- a/res/res_pjsip_transport_management.c
+++ b/res/res_pjsip_transport_management.c
@@ -42,7 +42,7 @@
static const pj_str_t keepalive_packet = { "\r\n\r\n", 4 };
/*! \brief Global container of active transports */
-static struct ao2_container *transports;
+static AO2_GLOBAL_OBJ_STATIC(monitored_transports);
/*! \brief Scheduler context for timing out connections with no data received */
static struct ast_sched_context *sched;
@@ -84,6 +84,7 @@ static int keepalive_transport_cb(void *obj, void *arg, int flags)
/*! \brief Thread which sends keepalives to all active connection-oriented transports */
static void *keepalive_transport_thread(void *data)
{
+ struct ao2_container *transports;
pj_thread_desc desc;
pj_thread_t *thread;
@@ -92,6 +93,11 @@ static void *keepalive_transport_thread(void *data)
return NULL;
}
+ transports = ao2_global_obj_ref(monitored_transports);
+ if (!transports) {
+ 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.
*/
@@ -100,6 +106,7 @@ static void *keepalive_transport_thread(void *data)
ao2_callback(transports, OBJ_NODATA, keepalive_transport_cb, NULL);
}
+ ao2_ref(transports, -1);
return NULL;
}
@@ -108,7 +115,6 @@ AST_THREADSTORAGE(desc_storage);
static int idle_sched_cb(const void *data)
{
struct monitored_transport *keepalive = (struct monitored_transport *) data;
- int sip_received = ast_atomic_fetchadd_int(&keepalive->sip_received, 0);
if (!pj_thread_is_registered()) {
pj_thread_t *thread;
@@ -126,7 +132,7 @@ static int idle_sched_cb(const void *data)
pj_thread_register("Transport Monitor", *desc, &thread);
}
- if (!sip_received) {
+ if (!keepalive->sip_received) {
ast_log(LOG_NOTICE, "Shutting down transport '%s' since no request was received in %d seconds\n",
keepalive->transport->info, IDLE_TIMEOUT);
pjsip_transport_shutdown(keepalive->transport);
@@ -148,23 +154,30 @@ static void monitored_transport_destroy(void *obj)
static void monitored_transport_state_callback(pjsip_transport *transport, pjsip_transport_state state,
const pjsip_transport_state_info *info)
{
+ struct ao2_container *transports;
+
/* We only care about reliable transports */
- if (PJSIP_TRANSPORT_IS_RELIABLE(transport) &&
- (transport->dir == PJSIP_TP_DIR_INCOMING || keepalive_interval)) {
+ if (PJSIP_TRANSPORT_IS_RELIABLE(transport)
+ && (transport->dir == PJSIP_TP_DIR_INCOMING || keepalive_interval)
+ && (transports = ao2_global_obj_ref(monitored_transports))) {
struct monitored_transport *monitored;
switch (state) {
case PJSIP_TP_STATE_CONNECTED:
- monitored = ao2_alloc(sizeof(*monitored), monitored_transport_destroy);
+ monitored = ao2_alloc_options(sizeof(*monitored),
+ monitored_transport_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK);
if (!monitored) {
break;
}
monitored->transport = transport;
pjsip_transport_add_ref(monitored->transport);
+
ao2_link(transports, monitored);
+
if (transport->dir == PJSIP_TP_DIR_INCOMING) {
/* Let the scheduler inherit the reference from allocation */
if (ast_sched_add_variable(sched, IDLE_TIMEOUT, idle_sched_cb, monitored, 1) < 0) {
+ /* Uh Oh. Could not schedule the idle check. Kill the transport. */
ao2_unlink(transports, monitored);
ao2_ref(monitored, -1);
pjsip_transport_shutdown(transport);
@@ -181,6 +194,8 @@ static void monitored_transport_state_callback(pjsip_transport *transport, pjsip
default:
break;
}
+
+ ao2_ref(transports, -1);
}
/* Forward to the old state callback if present */
@@ -242,7 +257,7 @@ static int monitored_transport_cmp_fn(void *obj, void *arg, int flags)
break;
}
- return !cmp ? CMP_MATCH | CMP_STOP : 0;
+ return !cmp ? CMP_MATCH : 0;
}
static void keepalive_global_loaded(const char *object_type)
@@ -265,8 +280,8 @@ static void keepalive_global_loaded(const char *object_type)
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;
+ keepalive_thread = AST_PTHREADT_NULL;
+ keepalive_interval = 0;
}
}
@@ -283,14 +298,21 @@ static struct ast_sorcery_observer keepalive_global_observer = {
*/
static pj_bool_t idle_monitor_on_rx_request(pjsip_rx_data *rdata)
{
+ struct ao2_container *transports;
struct monitored_transport *idle_trans;
+ transports = ao2_global_obj_ref(monitored_transports);
+ if (!transports) {
+ return PJ_FALSE;
+ }
+
idle_trans = ao2_find(transports, rdata->tp_info.transport->obj_name, OBJ_SEARCH_KEY);
+ ao2_ref(transports, -1);
if (!idle_trans) {
return PJ_FALSE;
}
- ast_atomic_fetchadd_int(&idle_trans->sip_received, +1);
+ idle_trans->sip_received = 1;
ao2_ref(idle_trans, -1);
return PJ_FALSE;
@@ -304,35 +326,38 @@ static pjsip_module idle_monitor_module = {
static int load_module(void)
{
+ struct ao2_container *transports;
pjsip_tpmgr *tpmgr;
CHECK_PJSIP_MODULE_LOADED();
+ tpmgr = pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint());
+ if (!tpmgr) {
+ ast_log(LOG_ERROR, "No transport manager to attach keepalive functionality to.\n");
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
transports = ao2_container_alloc(TRANSPORTS_BUCKETS, monitored_transport_hash_fn,
monitored_transport_cmp_fn);
if (!transports) {
ast_log(LOG_ERROR, "Could not create container for transports to perform keepalive on.\n");
return AST_MODULE_LOAD_DECLINE;
}
-
- 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 AST_MODULE_LOAD_DECLINE;
- }
+ ao2_global_obj_replace_unref(monitored_transports, transports);
+ ao2_ref(transports, -1);
sched = ast_sched_context_create();
if (!sched) {
ast_log(LOG_ERROR, "Failed to create keepalive scheduler context.\n");
- ao2_ref(transports, -1);
+ ao2_global_obj_release(monitored_transports);
return AST_MODULE_LOAD_DECLINE;
}
if (ast_sched_start_thread(sched)) {
ast_log(LOG_ERROR, "Failed to start keepalive scheduler thread\n");
ast_sched_context_destroy(sched);
- ao2_ref(transports, -1);
+ sched = NULL;
+ ao2_global_obj_release(monitored_transports);
return AST_MODULE_LOAD_DECLINE;
}
@@ -343,25 +368,38 @@ static int load_module(void)
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)
{
- pjsip_tpmgr *tpmgr = pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint());
+ pjsip_tpmgr *tpmgr;
if (keepalive_interval) {
keepalive_interval = 0;
- pthread_kill(keepalive_thread, SIGURG);
- pthread_join(keepalive_thread, NULL);
+ if (keepalive_thread != AST_PTHREADT_NULL) {
+ pthread_kill(keepalive_thread, SIGURG);
+ pthread_join(keepalive_thread, NULL);
+ keepalive_thread = AST_PTHREADT_NULL;
+ }
}
- ast_sched_context_destroy(sched);
- ao2_ref(transports, -1);
+ ast_sorcery_observer_remove(ast_sip_get_sorcery(), "global", &keepalive_global_observer);
+
+ tpmgr = pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint());
+ if (tpmgr) {
+ pjsip_tpmgr_set_state_cb(tpmgr, tpmgr_state_callback);
+ }
ast_sip_unregister_service(&idle_monitor_module);
- pjsip_tpmgr_set_state_cb(tpmgr, tpmgr_state_callback);
+
+ ast_sched_context_destroy(sched);
+ sched = NULL;
+
+ ao2_global_obj_release(monitored_transports);
+
return 0;
}
@@ -372,9 +410,9 @@ static int reload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Reliable Transport Management",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .reload = reload_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_CHANNEL_DEPEND - 4,
- );
+ .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_rtp_asterisk.c b/res/res_rtp_asterisk.c
index b2ecf5962..d05774fde 100644
--- a/res/res_rtp_asterisk.c
+++ b/res/res_rtp_asterisk.c
@@ -407,6 +407,12 @@ struct ast_rtcp {
#ifdef HAVE_OPENSSL_SRTP
struct dtls_details dtls; /*!< DTLS state information */
#endif
+
+ /* Cached local address string allows us to generate
+ * RTCP stasis messages without having to look up our
+ * own address every time
+ */
+ char *local_addr_str;
};
struct rtp_red {
@@ -540,8 +546,8 @@ static int ice_candidate_cmp(void *obj, void *arg, int flags)
if (strcmp(candidate1->foundation, candidate2->foundation) ||
candidate1->id != candidate2->id ||
- ast_sockaddr_cmp(&candidate1->address, &candidate2->address) ||
- candidate1->type != candidate1->type) {
+ candidate1->type != candidate2->type ||
+ ast_sockaddr_cmp(&candidate1->address, &candidate2->address)) {
return 0;
}
@@ -1340,7 +1346,7 @@ 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
+#ifdef HAVE_OPENSSL_EC
EC_KEY *ecdh;
#endif
@@ -1357,21 +1363,53 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con
return 0;
}
- if (!(rtp->ssl_ctx = SSL_CTX_new(DTLSv1_method()))) {
+#if OPENSSL_VERSION_NUMBER < 0x10002000L
+ rtp->ssl_ctx = SSL_CTX_new(DTLSv1_method());
+#else
+ rtp->ssl_ctx = SSL_CTX_new(DTLS_method());
+#endif
+ if (!rtp->ssl_ctx) {
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
+#ifdef HAVE_OPENSSL_EC
+
+ if (!ast_strlen_zero(dtls_cfg->pvtfile)) {
+ BIO *bio = BIO_new_file(dtls_cfg->pvtfile, "r");
+ if (bio != NULL) {
+ DH *dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
+ if (dh != NULL) {
+ if (SSL_CTX_set_tmp_dh(rtp->ssl_ctx, dh)) {
+ long options = SSL_OP_CIPHER_SERVER_PREFERENCE |
+ SSL_OP_SINGLE_DH_USE | SSL_OP_SINGLE_ECDH_USE;
+ options = SSL_CTX_set_options(rtp->ssl_ctx, options);
+ ast_verb(2, "DTLS DH initialized, PFS enabled\n");
+ }
+ DH_free(dh);
+ }
+ BIO_free(bio);
+ }
+ }
+ /* enables AES-128 ciphers, to get AES-256 use NID_secp384r1 */
ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
- if (ecdh) {
- SSL_CTX_set_tmp_ecdh(rtp->ssl_ctx, ecdh);
+ if (ecdh != NULL) {
+ if (SSL_CTX_set_tmp_ecdh(rtp->ssl_ctx, ecdh)) {
+ #ifndef SSL_CTRL_SET_ECDH_AUTO
+ #define SSL_CTRL_SET_ECDH_AUTO 94
+ #endif
+ /* SSL_CTX_set_ecdh_auto(rtp->ssl_ctx, on); requires OpenSSL 1.0.2 which wraps: */
+ if (SSL_CTX_ctrl(rtp->ssl_ctx, SSL_CTRL_SET_ECDH_AUTO, 1, NULL)) {
+ ast_verb(2, "DTLS ECDH initialized (automatic), faster PFS enabled\n");
+ } else {
+ ast_verb(2, "DTLS ECDH initialized (secp256r1), faster PFS enabled\n");
+ }
+ }
EC_KEY_free(ecdh);
}
-#endif
+
+#endif /* #ifdef HAVE_OPENSSL_EC */
rtp->dtls_verify = dtls_cfg->verify;
@@ -1393,7 +1431,7 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con
if (!ast_strlen_zero(dtls_cfg->certfile)) {
char *private = ast_strlen_zero(dtls_cfg->pvtfile) ? dtls_cfg->certfile : dtls_cfg->pvtfile;
BIO *certbio;
- X509 *cert;
+ X509 *cert = NULL;
const EVP_MD *type;
unsigned int size, i;
unsigned char fingerprint[EVP_MAX_MD_SIZE];
@@ -1435,6 +1473,9 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con
ast_log(LOG_ERROR, "Could not produce fingerprint from certificate '%s' for RTP instance '%p'\n",
dtls_cfg->certfile, instance);
BIO_free_all(certbio);
+ if (cert) {
+ X509_free(cert);
+ }
return -1;
}
@@ -1446,6 +1487,7 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con
*(local_fingerprint-1) = 0;
BIO_free_all(certbio);
+ X509_free(cert);
}
if (!ast_strlen_zero(dtls_cfg->cipher)) {
@@ -2556,7 +2598,7 @@ static int ast_rtp_new(struct ast_rtp_instance *instance,
/* Set default parameters on the newly created RTP structure */
rtp->ssrc = ast_random();
- rtp->seqno = ast_random() & 0xffff;
+ rtp->seqno = ast_random() & 0x7fff;
rtp->strict_rtp_state = (strictrtp ? STRICT_RTP_LEARN : STRICT_RTP_OPEN);
if (strictrtp) {
rtp_learning_seq_init(&rtp->rtp_source_learn, (uint16_t)rtp->seqno);
@@ -2664,6 +2706,7 @@ static int ast_rtp_destroy(struct ast_rtp_instance *instance)
* RTP instance while it's active.
*/
close(rtp->rtcp->s);
+ ast_free(rtp->rtcp->local_addr_str);
ast_free(rtp->rtcp);
}
@@ -3076,12 +3119,7 @@ static int ast_rtcp_write_report(struct ast_rtp_instance *instance, int sr)
int rate = rtp_get_rate(rtp->f.subclass.format);
int ice;
int header_offset = 0;
- char *str_remote_address;
- char *str_local_address;
struct ast_sockaddr remote_address = { { 0, } };
- struct ast_sockaddr local_address = { { 0, } };
- struct ast_sockaddr real_remote_address = { { 0, } };
- struct ast_sockaddr real_local_address = { { 0, } };
struct ast_rtp_rtcp_report_block *report_block = NULL;
RAII_VAR(struct ast_rtp_rtcp_report *, rtcp_report,
ast_rtp_rtcp_report_alloc(rtp->themssrc ? 1 : 0),
@@ -3208,22 +3246,9 @@ static int ast_rtcp_write_report(struct ast_rtp_instance *instance, int sr)
}
}
- ast_rtp_instance_get_local_address(instance, &local_address);
- if (!ast_find_ourip(&real_local_address, &local_address, 0)) {
- str_local_address = ast_strdupa(ast_sockaddr_stringify(&real_local_address));
- } else {
- str_local_address = ast_strdupa(ast_sockaddr_stringify(&local_address));
- }
-
- if (!ast_find_ourip(&real_remote_address, &remote_address, 0)) {
- str_remote_address = ast_strdupa(ast_sockaddr_stringify(&real_remote_address));
- } else {
- str_remote_address = ast_strdupa(ast_sockaddr_stringify(&remote_address));
- }
-
message_blob = ast_json_pack("{s: s, s: s}",
- "to", str_remote_address,
- "from", str_local_address);
+ "to", ast_sockaddr_stringify(&remote_address),
+ "from", rtp->rtcp->local_addr_str);
ast_rtp_publish_rtcp_message(instance, ast_rtp_rtcp_sent_type(),
rtcp_report,
message_blob);
@@ -4033,11 +4058,6 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
int report_counter = 0;
struct ast_rtp_rtcp_report_block *report_block;
struct ast_frame *f = &ast_null_frame;
- char *str_local_address;
- char *str_remote_address;
- struct ast_sockaddr local_address = { { 0,} };
- struct ast_sockaddr real_local_address = { { 0, } };
- struct ast_sockaddr real_remote_address = { { 0, } };
/* Read in RTCP data from the socket */
if ((res = rtcp_recvfrom(instance, rtcpdata + AST_FRIENDLY_OFFSET,
@@ -4094,8 +4114,6 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
ast_debug(1, "Got RTCP report of %d bytes\n", res);
- ast_rtp_instance_get_local_address(instance, &local_address);
-
while (position < packetwords) {
int i, pt, rc;
unsigned int length;
@@ -4211,21 +4229,10 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
/* If and when we handle more than one report block, this should occur outside
* this loop.
*/
- if (!ast_find_ourip(&real_local_address, &local_address, 0)) {
- str_local_address = ast_strdupa(ast_sockaddr_stringify(&real_local_address));
- } else {
- str_local_address = ast_strdupa(ast_sockaddr_stringify(&local_address));
- }
-
- if (!ast_find_ourip(&real_remote_address, &addr, 0)) {
- str_remote_address = ast_strdupa(ast_sockaddr_stringify(&real_remote_address));
- } else {
- str_remote_address = ast_strdupa(ast_sockaddr_stringify(&addr));
- }
message_blob = ast_json_pack("{s: s, s: s, s: f}",
- "from", str_remote_address,
- "to", str_local_address,
+ "from", ast_sockaddr_stringify(&rtp->rtcp->them),
+ "to", rtp->rtcp->local_addr_str,
"rtt", rtp->rtcp->rtt);
ast_rtp_publish_rtcp_message(instance, ast_rtp_rtcp_received_type(),
rtcp_report,
@@ -4787,6 +4794,8 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro
if (property == AST_RTP_PROPERTY_RTCP) {
if (value) {
+ struct ast_sockaddr local_addr;
+
if (rtp->rtcp) {
ast_debug(1, "Ignoring duplicate RTCP property on RTP instance '%p'\n", instance);
return;
@@ -4801,6 +4810,19 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro
ast_sockaddr_set_port(&rtp->rtcp->us,
ast_sockaddr_port(&rtp->rtcp->us) + 1);
+ if (!ast_find_ourip(&local_addr, &rtp->rtcp->us, 0)) {
+ ast_sockaddr_set_port(&local_addr, ast_sockaddr_port(&rtp->rtcp->us));
+ } else {
+ ast_sockaddr_copy(&local_addr, &rtp->rtcp->us);
+ }
+
+ rtp->rtcp->local_addr_str = ast_strdup(ast_sockaddr_stringify(&local_addr));
+ if (!rtp->rtcp->local_addr_str) {
+ ast_free(rtp->rtcp);
+ rtp->rtcp = NULL;
+ return;
+ }
+
if ((rtp->rtcp->s =
create_new_socket("RTCP",
ast_sockaddr_is_ipv4(&rtp->rtcp->us) ?
@@ -4808,6 +4830,7 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro
ast_sockaddr_is_ipv6(&rtp->rtcp->us) ?
AF_INET6 : -1)) < 0) {
ast_debug(1, "Failed to create a new socket for RTCP on instance '%p'\n", instance);
+ ast_free(rtp->rtcp->local_addr_str);
ast_free(rtp->rtcp);
rtp->rtcp = NULL;
return;
@@ -4817,6 +4840,7 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro
if (ast_bind(rtp->rtcp->s, &rtp->rtcp->us)) {
ast_debug(1, "Failed to setup RTCP on RTP instance '%p'\n", instance);
close(rtp->rtcp->s);
+ ast_free(rtp->rtcp->local_addr_str);
ast_free(rtp->rtcp);
rtp->rtcp = NULL;
return;
@@ -4856,6 +4880,7 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro
SSL_free(rtp->rtcp->dtls.ssl);
}
#endif
+ ast_free(rtp->rtcp->local_addr_str);
ast_free(rtp->rtcp);
rtp->rtcp = NULL;
}
diff --git a/res/res_sorcery_config.c b/res/res_sorcery_config.c
index 220b5875f..056f63eaa 100644
--- a/res/res_sorcery_config.c
+++ b/res/res_sorcery_config.c
@@ -210,6 +210,10 @@ static void sorcery_config_retrieve_regex(const struct ast_sorcery *sorcery, voi
.regex = &expression,
};
+ if (ast_strlen_zero(regex)) {
+ regex = ".";
+ }
+
if (!config_objects || regcomp(&expression, regex, REG_EXTENDED | REG_NOSUB)) {
return;
}
diff --git a/res/res_sorcery_memory.c b/res/res_sorcery_memory.c
index e8f603038..b2f05591b 100644
--- a/res/res_sorcery_memory.c
+++ b/res/res_sorcery_memory.c
@@ -188,6 +188,10 @@ static void sorcery_memory_retrieve_regex(const struct ast_sorcery *sorcery, voi
.regex = &expression,
};
+ if (ast_strlen_zero(regex)) {
+ regex = ".";
+ }
+
if (regcomp(&expression, regex, REG_EXTENDED | REG_NOSUB)) {
return;
}
diff --git a/res/res_sorcery_realtime.c b/res/res_sorcery_realtime.c
index 2c533ea0b..138d6ea95 100644
--- a/res/res_sorcery_realtime.c
+++ b/res/res_sorcery_realtime.c
@@ -271,7 +271,7 @@ static int sorcery_realtime_update(const struct ast_sorcery *sorcery, void *data
return -1;
}
- return (ast_update_realtime_fields(config->family, UUID_FIELD, ast_sorcery_object_get_id(object), fields) <= 0) ? -1 : 0;
+ return (ast_update_realtime_fields(config->family, UUID_FIELD, ast_sorcery_object_get_id(object), fields) < 0) ? -1 : 0;
}
static int sorcery_realtime_delete(const struct ast_sorcery *sorcery, void *data, void *object)
diff --git a/res/res_stasis.c b/res/res_stasis.c
index fae9aa220..11aeb438e 100644
--- a/res/res_stasis.c
+++ b/res/res_stasis.c
@@ -713,6 +713,22 @@ int stasis_app_bridge_playback_channel_add(struct ast_bridge *bridge,
return 0;
}
+void stasis_app_bridge_playback_channel_remove(char *bridge_id,
+ struct stasis_app_control *control)
+{
+ struct stasis_app_bridge_channel_wrapper *wrapper;
+
+ wrapper = ao2_find(app_bridges_playback, bridge_id, OBJ_SEARCH_KEY | OBJ_UNLINK);
+ if (wrapper) {
+ /* If wrapper is not found, then that means the after bridge callback has been
+ * called or is in progress. No need to unlink the control here since that has
+ * been done or is about to be done in the after bridge callback
+ */
+ ao2_unlink(app_controls, control);
+ ao2_ref(wrapper, -1);
+ }
+}
+
struct ast_channel *stasis_app_bridge_playback_channel_find(struct ast_bridge *bridge)
{
struct stasis_app_bridge_channel_wrapper *playback_wrapper;
diff --git a/res/res_xmpp.c b/res/res_xmpp.c
index ed35cd169..d7f2b5478 100644
--- a/res/res_xmpp.c
+++ b/res/res_xmpp.c
@@ -4652,6 +4652,10 @@ static int load_module(void)
ast_mutex_init(&messagelock);
ast_cond_init(&message_received_condition, NULL);
+ if (ast_eid_is_empty(&ast_eid_default)) {
+ ast_log(LOG_WARNING, "Entity ID is not set. The distributing device state or MWI will not work.\n");
+ }
+
return AST_MODULE_LOAD_SUCCESS;
}
diff --git a/tests/test_res_pjsip_scheduler.c b/tests/test_res_pjsip_scheduler.c
index f9a1633ac..a5461accb 100644
--- a/tests/test_res_pjsip_scheduler.c
+++ b/tests/test_res_pjsip_scheduler.c
@@ -26,6 +26,7 @@
/*** MODULEINFO
<depend>TEST_FRAMEWORK</depend>
+ <depend>pjproject</depend>
<depend>res_pjsip</depend>
<support_level>core</support_level>
***/
diff --git a/tests/test_sorcery_realtime.c b/tests/test_sorcery_realtime.c
index 3ed14623e..64475fb8b 100644
--- a/tests/test_sorcery_realtime.c
+++ b/tests/test_sorcery_realtime.c
@@ -711,41 +711,6 @@ AST_TEST_DEFINE(object_update)
return AST_TEST_PASS;
}
-AST_TEST_DEFINE(object_update_uncreated)
-{
- RAII_VAR(struct ast_sorcery *, sorcery, NULL, deinitialize_sorcery);
- RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
-
- switch (cmd) {
- case TEST_INIT:
- info->name = "object_update_uncreated";
- info->category = "/res/sorcery_realtime/";
- info->summary = "sorcery object update unit test";
- info->description =
- "Test updating of an uncreated object in sorcery using realtime wizard";
- return AST_TEST_NOT_RUN;
- case TEST_EXECUTE:
- break;
- }
-
- if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test"))) {
- ast_test_status_update(test, "Failed to open sorcery structure\n");
- return AST_TEST_FAIL;
- }
-
- if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
- ast_test_status_update(test, "Failed to allocate a known object type\n");
- return AST_TEST_FAIL;
- }
-
- if (!ast_sorcery_update(sorcery, obj)) {
- ast_test_status_update(test, "Successfully updated an object which has not been created yet\n");
- return AST_TEST_FAIL;
- }
-
- return AST_TEST_PASS;
-}
-
AST_TEST_DEFINE(object_delete)
{
RAII_VAR(struct ast_sorcery *, sorcery, NULL, deinitialize_sorcery);
@@ -942,7 +907,6 @@ static int unload_module(void)
AST_TEST_UNREGISTER(object_retrieve_regex);
AST_TEST_UNREGISTER(object_retrieve_regex_nofetch);
AST_TEST_UNREGISTER(object_update);
- AST_TEST_UNREGISTER(object_update_uncreated);
AST_TEST_UNREGISTER(object_delete);
AST_TEST_UNREGISTER(object_delete_uncreated);
AST_TEST_UNREGISTER(object_allocate_on_retrieval);
@@ -964,7 +928,6 @@ static int load_module(void)
AST_TEST_REGISTER(object_retrieve_regex);
AST_TEST_REGISTER(object_retrieve_regex_nofetch);
AST_TEST_REGISTER(object_update);
- AST_TEST_REGISTER(object_update_uncreated);
AST_TEST_REGISTER(object_delete);
AST_TEST_REGISTER(object_delete_uncreated);
AST_TEST_REGISTER(object_allocate_on_retrieval);
diff --git a/third-party/pjproject/Makefile.rules b/third-party/pjproject/Makefile.rules
index d2e7d25a4..739193aca 100644
--- a/third-party/pjproject/Makefile.rules
+++ b/third-party/pjproject/Makefile.rules
@@ -1,9 +1,34 @@
PJPROJECT_URL = http://www.pjsip.org/release/$(PJPROJECT_VERSION)
# Even though we're not installing pjproject, we're setting prefix to /opt/pjproject to be safe
-PJPROJECT_CONFIG_OPTS = --prefix=/opt/pjproject --disable-speex-codec --disable-speex-aec \
- --disable-gsm-codec --disable-video --disable-v4l2 --disable-sound --disable-opencore-amr \
- --disable-ilbc-codec --without-libyuv --disable-g7221-codec --disable-resample
+
+PJPROJECT_CONFIG_OPTS = --prefix=/opt/pjproject \
+ --disable-speex-codec \
+ --disable-speex-aec \
+ --disable-speex-aec \
+ --disable-gsm-codec \
+ --disable-ilbc-codec \
+ --disable-l16-codec \
+ --disable-g711-codec \
+ --disable-g722-codec \
+ --disable-g7221-codec \
+ --disable-opencore-amr \
+ --disable-webrtc \
+ --disable-silk \
+ --disable-opus \
+ --disable-video \
+ --disable-v4l2 \
+ --disable-sound \
+ --disable-ext-sound \
+ --disable-oss \
+ --disable-sdl \
+ --disable-libyuv \
+ --disable-resample \
+ --disable-ffmpeg \
+ --disable-openh264 \
+ --disable-ipp \
+ --without-external-pa \
+ --with-external-srtp
ifeq ($(shell uname -s),Linux)
PJPROJECT_CONFIG_OPTS += --enable-epoll
diff --git a/third-party/pjproject/configure.m4 b/third-party/pjproject/configure.m4
index 2cc18bfa8..67ac04d4d 100644
--- a/third-party/pjproject/configure.m4
+++ b/third-party/pjproject/configure.m4
@@ -44,4 +44,5 @@ AC_DEFUN([PJPROJECT_CONFIGURE],
PJPROJECT_SYMBOL_CHECK([PJ_SSL_CERT_LOAD_FROM_FILES2], [pj_ssl_cert_load_from_files2], [pjlib.h])
PJPROJECT_SYMBOL_CHECK([PJSIP_EXTERNAL_RESOLVER], [pjsip_endpt_set_ext_resolver], [pjsip.h])
AC_DEFINE([HAVE_PJSIP_TLS_TRANSPORT_PROTO], 1, [Define if your system has PJSIP_TLS_TRANSPORT_PROTO])
+ AC_DEFINE([HAVE_PJSIP_EVSUB_GRP_LOCK], 1, [Define if your system has PJSIP_EVSUB_GRP_LOCK])
])
diff --git a/third-party/pjproject/patches/0001-r5397-pjsip_generic_array_max_count.patch b/third-party/pjproject/patches/0001-r5397-pjsip_generic_array_max_count.patch
new file mode 100644
index 000000000..3cc328afe
--- /dev/null
+++ b/third-party/pjproject/patches/0001-r5397-pjsip_generic_array_max_count.patch
@@ -0,0 +1,58 @@
+This patch updates array limit checks and docs
+in pjsip_evsub_register_pkg() and pjsip_endpt_add_capability().
+
+Index: pjsip/include/pjsip/sip_endpoint.h
+===================================================================
+--- a/pjsip/include/pjsip/sip_endpoint.h (revision 5396)
++++ b/pjsip/include/pjsip/sip_endpoint.h (revision 5397)
+@@ -583,7 +583,8 @@
+ * @param hname If htype specifies PJSIP_H_OTHER, then the header name
+ * must be supplied in this argument. Otherwise the value
+ * must be set to NULL.
+- * @param count The number of tags in the array.
++ * @param count The number of tags in the array. The value must not
++ * be greater than PJSIP_GENERIC_ARRAY_MAX_COUNT.
+ * @param tags Array of tags describing the capabilities or extensions
+ * to be added to the appropriate header.
+ *
+Index: pjsip/include/pjsip-simple/evsub.h
+===================================================================
+--- a/pjsip/include/pjsip-simple/evsub.h (revision 5396)
++++ b/pjsip/include/pjsip-simple/evsub.h (revision 5397)
+@@ -246,7 +246,8 @@
+ * registered.
+ * @param event_name Event package identification.
+ * @param expires Default subscription expiration time, in seconds.
+- * @param accept_cnt Number of strings in Accept array.
++ * @param accept_cnt Number of strings in Accept array. The value must
++ * not be greater than PJSIP_GENERIC_ARRAY_MAX_COUNT.
+ * @param accept Array of Accept value.
+ *
+ * @return PJ_SUCCESS on success.
+Index: pjsip/src/pjsip/sip_endpoint.c
+===================================================================
+--- a/pjsip/src/pjsip/sip_endpoint.c (revision 5396)
++++ b/pjsip/src/pjsip/sip_endpoint.c (revision 5397)
+@@ -371,6 +371,7 @@
+
+ /* Check arguments. */
+ PJ_ASSERT_RETURN(endpt!=NULL && count>0 && tags, PJ_EINVAL);
++ PJ_ASSERT_RETURN(count <= PJSIP_GENERIC_ARRAY_MAX_COUNT, PJ_ETOOMANY);
+ PJ_ASSERT_RETURN(htype==PJSIP_H_ACCEPT ||
+ htype==PJSIP_H_ALLOW ||
+ htype==PJSIP_H_SUPPORTED,
+Index: pjsip/src/pjsip-simple/evsub.c
+===================================================================
+--- a/pjsip/src/pjsip-simple/evsub.c (revision 5396)
++++ b/pjsip/src/pjsip-simple/evsub.c (revision 5397)
+@@ -412,7 +412,9 @@
+ unsigned i;
+
+ PJ_ASSERT_RETURN(pkg_mod && event_name, PJ_EINVAL);
+- PJ_ASSERT_RETURN(accept_cnt < PJ_ARRAY_SIZE(pkg->pkg_accept->values),
++
++ /* Make sure accept_cnt < PJ_ARRAY_SIZE(pkg->pkg_accept->values) */
++ PJ_ASSERT_RETURN(accept_cnt <= PJSIP_GENERIC_ARRAY_MAX_COUNT,
+ PJ_ETOOMANY);
+
+ /* Make sure evsub module has been initialized */
diff --git a/third-party/pjproject/patches/0001-r5400-pjsip_tx_data_dec_ref.patch b/third-party/pjproject/patches/0001-r5400-pjsip_tx_data_dec_ref.patch
new file mode 100644
index 000000000..b5c11db45
--- /dev/null
+++ b/third-party/pjproject/patches/0001-r5400-pjsip_tx_data_dec_ref.patch
@@ -0,0 +1,24 @@
+This patch fixes the issue in pjsip_tx_data_dec_ref()
+when tx_data_destroy can be called more than once,
+and checks if invalid value (e.g. NULL) is passed to.
+
+Index: pjsip/src/pjsip/sip_transport.c
+===================================================================
+--- a/pjsip/src/pjsip/sip_transport.c (revision 5399)
++++ b/pjsip/src/pjsip/sip_transport.c (revision 5400)
+@@ -491,8 +491,13 @@
+ */
+ PJ_DEF(pj_status_t) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata )
+ {
+- pj_assert( pj_atomic_get(tdata->ref_cnt) > 0);
+- if (pj_atomic_dec_and_get(tdata->ref_cnt) <= 0) {
++ pj_atomic_value_t ref_cnt;
++
++ PJ_ASSERT_RETURN(tdata && tdata->ref_cnt, PJ_EINVAL);
++
++ ref_cnt = pj_atomic_dec_and_get(tdata->ref_cnt);
++ pj_assert( ref_cnt >= 0);
++ if (ref_cnt == 0) {
+ tx_data_destroy(tdata);
+ return PJSIP_EBUFDESTROYED;
+ } else {
diff --git a/third-party/pjproject/patches/0001-sip_transport_tcp-tls-Set-factory-on-transports-crea.patch b/third-party/pjproject/patches/0001-sip_transport_tcp-tls-Set-factory-on-transports-crea.patch
deleted file mode 100644
index d8a9adbde..000000000
--- a/third-party/pjproject/patches/0001-sip_transport_tcp-tls-Set-factory-on-transports-crea.patch
+++ /dev/null
@@ -1,48 +0,0 @@
-From b7cb93b0e1729589a71e8b30d9a9893f0918e2a2 Mon Sep 17 00:00:00 2001
-From: George Joseph <george.joseph@fairview5.com>
-Date: Mon, 30 May 2016 11:58:22 -0600
-Subject: [PATCH] sip_transport_tcp/tls: Set factory on transports created
- from accept
-
-The ability to re-use tcp and tls transports when a factory is
-specified now depends on transport->factory being set which is a new field
-in 2.5. This was being set only on new outgoing sockets not on
-incoming sockets. The result was that a client REGISTER created a new
-socket but without the factory set, the next outgoing request to the
-client, OPTIONS, INVITE, etc, would attempt to create another socket
-which the client would refuse.
-
-This patch sets the factory on transports created as a result of an
-accept.
----
- pjsip/src/pjsip/sip_transport_tcp.c | 1 +
- pjsip/src/pjsip/sip_transport_tls.c | 1 +
- 2 files changed, 2 insertions(+)
-
-diff --git a/pjsip/src/pjsip/sip_transport_tcp.c b/pjsip/src/pjsip/sip_transport_tcp.c
-index 1bbb324..00eb8fc 100644
---- a/pjsip/src/pjsip/sip_transport_tcp.c
-+++ b/pjsip/src/pjsip/sip_transport_tcp.c
-@@ -713,6 +713,7 @@ static pj_status_t tcp_create( struct tcp_listener *listener,
- tcp->base.send_msg = &tcp_send_msg;
- tcp->base.do_shutdown = &tcp_shutdown;
- tcp->base.destroy = &tcp_destroy_transport;
-+ tcp->base.factory = &listener->factory;
-
- /* Create group lock */
- status = pj_grp_lock_create(pool, NULL, &tcp->grp_lock);
-diff --git a/pjsip/src/pjsip/sip_transport_tls.c b/pjsip/src/pjsip/sip_transport_tls.c
-index a83ac32..36ee70d 100644
---- a/pjsip/src/pjsip/sip_transport_tls.c
-+++ b/pjsip/src/pjsip/sip_transport_tls.c
-@@ -742,6 +742,7 @@ static pj_status_t tls_create( struct tls_listener *listener,
- tls->base.send_msg = &tls_send_msg;
- tls->base.do_shutdown = &tls_shutdown;
- tls->base.destroy = &tls_destroy_transport;
-+ tls->base.factory = &listener->factory;
-
- tls->ssock = ssock;
-
---
-2.5.5
-
diff --git a/third-party/pjproject/patches/0002-Fix-1946-Avoid-deinitialization-of-uninitialized-cli.patch b/third-party/pjproject/patches/0002-Fix-1946-Avoid-deinitialization-of-uninitialized-cli.patch
new file mode 100644
index 000000000..c4288a389
--- /dev/null
+++ b/third-party/pjproject/patches/0002-Fix-1946-Avoid-deinitialization-of-uninitialized-cli.patch
@@ -0,0 +1,56 @@
+From 33fd755e819dc85a96718abc0ae26a9b46f14800 Mon Sep 17 00:00:00 2001
+From: nanang <nanang@localhost>
+Date: Thu, 28 Jul 2016 08:21:45 +0000
+Subject: [PATCH 2/3] Fix #1946: Avoid deinitialization of uninitialized client
+ auth session.
+
+---
+ pjsip/src/pjsip/sip_dialog.c | 18 ++++++------------
+ 1 file changed, 6 insertions(+), 12 deletions(-)
+
+diff --git a/pjsip/src/pjsip/sip_dialog.c b/pjsip/src/pjsip/sip_dialog.c
+index f03885d..421ddc4 100644
+--- a/pjsip/src/pjsip/sip_dialog.c
++++ b/pjsip/src/pjsip/sip_dialog.c
+@@ -92,6 +92,12 @@ static pj_status_t create_dialog( pjsip_user_agent *ua,
+ pj_list_init(&dlg->inv_hdr);
+ pj_list_init(&dlg->rem_cap_hdr);
+
++ /* Init client authentication session. */
++ status = pjsip_auth_clt_init(&dlg->auth_sess, dlg->endpt,
++ dlg->pool, 0);
++ if (status != PJ_SUCCESS)
++ goto on_error;
++
+ status = pj_mutex_create_recursive(pool, dlg->obj_name, &dlg->mutex_);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+@@ -283,12 +289,6 @@ PJ_DEF(pj_status_t) pjsip_dlg_create_uac( pjsip_user_agent *ua,
+ /* Initial route set is empty. */
+ pj_list_init(&dlg->route_set);
+
+- /* Init client authentication session. */
+- status = pjsip_auth_clt_init(&dlg->auth_sess, dlg->endpt,
+- dlg->pool, 0);
+- if (status != PJ_SUCCESS)
+- goto on_error;
+-
+ /* Register this dialog to user agent. */
+ status = pjsip_ua_register_dlg( ua, dlg );
+ if (status != PJ_SUCCESS)
+@@ -506,12 +506,6 @@ pj_status_t create_uas_dialog( pjsip_user_agent *ua,
+ }
+ dlg->route_set_frozen = PJ_TRUE;
+
+- /* Init client authentication session. */
+- status = pjsip_auth_clt_init(&dlg->auth_sess, dlg->endpt,
+- dlg->pool, 0);
+- if (status != PJ_SUCCESS)
+- goto on_error;
+-
+ /* Increment the dialog's lock since tsx may cause the dialog to be
+ * destroyed prematurely (such as in case of transport error).
+ */
+--
+2.7.4
+
diff --git a/third-party/pjproject/patches/config_site.h b/third-party/pjproject/patches/config_site.h
index 8e854b723..07e4d97a9 100644
--- a/third-party/pjproject/patches/config_site.h
+++ b/third-party/pjproject/patches/config_site.h
@@ -19,7 +19,7 @@
#define PJ_SCANNER_USE_BITWISE 0
#define PJ_OS_HAS_CHECK_STACK 0
#define PJ_LOG_MAX_LEVEL 3
-#define PJ_ENABLE_EXTRA_CHECK 0
+#define PJ_ENABLE_EXTRA_CHECK 1
#define PJSIP_MAX_TSX_COUNT ((64*1024)-1)
#define PJSIP_MAX_DIALOG_COUNT ((64*1024)-1)
#define PJSIP_UDP_SO_SNDBUF_SIZE (512*1024)
@@ -37,3 +37,7 @@
#undef PJ_TODO
#define PJ_TODO(x)
+
+/* Defaults too low for WebRTC */
+#define PJ_ICE_MAX_CAND 32
+#define PJ_ICE_MAX_CHECKS (PJ_ICE_MAX_CAND * 2)
diff --git a/third-party/versions.mak b/third-party/versions.mak
index ca40531df..a4d7874d5 100644
--- a/third-party/versions.mak
+++ b/third-party/versions.mak
@@ -1,2 +1,2 @@
-PJPROJECT_VERSION = 2.5
+PJPROJECT_VERSION = 2.5.5